I have this simple cmdlet that correctly copies files and folders to a second directory:
Copy-Item -Path 'G:\xyz\Test\A' -Recurse -Destination 'G:\xyz\Test\B\'
However I am unable to tweak it to only copy the latest file in each folder within its folder (i.e. also copying the folder structure). I have written the following, but this doesn't copy folder names and does not go down all the hierarchies of sub-folders.
Get-ChildItem -Path 'G:\xyz\Test\A' -Directory | ForEach-Object {
Get-ChildItem -Path 'G:\xyz\Test\A' -File -Recurse |
Sort-Object LastWriteTime | Select-Object -Last 1 |
Copy-Item -Destination 'G:\xyz\Test\B\'
}
Could someone please identify my errors!
CodePudding user response:
First error is that you don't pass the current directory from the outer Get-ChildItem to the inner one. The inner one currently always loops over the same sub directory.
Also, when you copy individual files in a Get-ChildItem pipeline, you have to build up the destination path on your own, in order to keep the relative source directory structure:
$source = 'G:\xyz\Test\A'
$destination = 'G:\xyz\Test\B\'
# Set base directory for outer Get-ChildItem and Resolve-Path -Relative
Push-Location $source
try {
Get-ChildItem -Directory | ForEach-Object {
Get-ChildItem -Path $_.Fullname -File -Recurse |
Sort-Object LastWriteTime | Select-Object -Last 1 |
ForEach-Object {
# Make the current file path relative to $source
$relativePath = Resolve-Path $_.Fullname -Relative
# Build up the full destination file path
$destinationFullPath = Join-Path $destination $relativePath
# Create destination directory if not already exists (-force)
$null = New-Item (Split-Path $destinationFullPath -Parent) -ItemType Directory -Force
# Copy the current file
Copy-Item -Path $_.Fullname -Destination $destinationFullPath
}
}
}
finally {
Pop-Location # Restore the current directory
}
Resolve-Path -Relativemakes the given path relative to the current directory. In order to get paths relative to the source directory, we set the current directory to the source directory path usingPush-Location.- The
tryandfinallyblocks make sure the current directory is restored even in case of a script-terminating error (exception).
CodePudding user response:
A slightly different approach of collecting the directories first then iterating through them to retrieve the files.
If ( -not (Test-Path -Path "G:\Test\B")) {
$Null = New-Item -ItemType "Directory" "G:\Test\B"
}
Get-ChildItem -Path 'G:\Test\A' -Directory -Recurse |
#Sort to insure dirs are created shortest to longest
Sort-Object FullName |
ForEach-Object {
$DestPath = $($_.FullName).Replace("`\Test`\A","`\Test\`B")
If ( -not (Test-Path -Path "$DestPath")) {
$Null = New-Item -ItemType "Directory" "$DestPath"
}
Get-ChildItem -Path "$($_.FullName)" -File |
Sort-Object LastWriteTime |
Select-Object -Last 1 |
Copy-Item -Destination "$DestPath"
}
