I'm working on a PowerShell script to automate file transfers using Robocopy. The script includes an ExcludeDirectories parameter, but directories specified in the exclusion list are not being excluded as expected. Here's the relevant portion of my code:
param (
[string]$LogFilePath
)
$usrdisk = "TESTGROUND011"
$logfile = $LogFilePath
$transln = "TransFolder"
function Robocopy {
param (
[string]$Source,
[string]$Destination,
[string[]]$ExcludeDirectories = @(),
[int]$MinAge = 0,
[bool]$Recursive = $true
)
$excludeParam = if ($ExcludeDirectories.Count -gt 0) { "/XD $($ExcludeDirectories | ForEach-Object { `" $_`" } -join ' ')" } else { "" }
$recursiveParam = if ($Recursive) { "/E" } else { "" } `enter code here`
$command = "robocopy `"$Source`" `"$Destination`" $recursiveParam /MOVE /MINAGE:$MinAge $excludeParam /NP /LOG+:`"$logfile`""
Invoke-Command $command
}
Robocopy "$usrdisk\$transln" "$usrdisk\$transln\delete" -ExcludeDirectories @("$usrdisk\$transln\Finance", "$usrdisk\$transln\Logistic", "$usrdisk\$transln\Pricing", "$usrdisk\$transln\StreamServe") -MinAge 7 -Recursive $true
Remove-Item "$usrdisk\$transln\delete" -Recurse -Force
What happens instead is the Powershell loops itself due to it searching its own Recurse Folder even if it should be Excluded.
I have tried to hardcode the /XD but it appears that powershell does not hand the code correctly to Robocopy.
Do you see any potential mistakes in my code?
This should solve your issues
The most important changes in the end are the (automatic) add of the Delete
directory to the excluded directories and using Invoke-Expression
instead of Invoke-Command
# let's start with a Approved Verb
function Invoke-Robocopy {
# parametrising all params will make things easier to check and expand.
param (
# Let's make sure the source directory exists
[ValidateScript({ Test-Path $_ })][Parameter(Mandatory)][string]$Source,
# Destination directory will be created so no need to check.
[Parameter(Mandatory)][string]$Destination,
# every other parameter is optional.
# Use SWITCH, not BOOL unless you specifically need a true\false value.
# Not this case.
[Parameter()][Switch]$Recursive,
[Parameter()][Switch]$Move,
[Parameter()][int]$MinAge,
[Parameter()][string[]]$ExcludeDirectories,
[Parameter()][switch]$NoProgress,
[parameter()][string]$LogFilePath,
[Parameter()][switch]$Silent
)
# Resolve the paths for maximum security.
$SourceParam = '"{0}"' -f (Resolve-Path $Source)
# using the parameter -Force if the destination directory doesn't exists, it gets created.
# otherwise it just get references.
$DestinationParam = '"{0}"' -f (New-Item -ItemType Directory $Destination -Force )
# .IsPresent is a parameter automatically added to Parameters.
# It checks $true if the parameter has been called with the command.
$recursiveParam = if ($Recursive.IsPresent) { '/E' }
$MoveParam = if ($Move.IsPresent) { '/MOVE' }
$MinAgeParam = if ($MinAge.IsPresent) { "/MINAGE:$MinAge" }
# Generally speaking, you always want to exclude the destination directory
# from the recursing.
# IDEALLY it should be done at the time of the parameter compilation, but
# let's dynamically add it here to the directory to exclude for good
# measure.
# it's not going to hurt even if the destination isn't actually in the
# recurse path.
$ExcludedDirResolved = foreach ($Directory in $ExcludeDirectories + $Destination) {
if ($ResolvedDirectory = Resolve-Path $Directory -ErrorAction SilentlyContinue) { '"{0}"' -f $ResolvedDirectory.Path }
}
$ExcludedDirParam = '/XD ' + $ExcludedDirResolved -join ' '
$NoProgressParam = if ($NoProgress) { '/NP' }
# If the log file doesn't exists, create it _AND_ its directories.
# Sadly the parameter -Force overwrite Files(but not directories!), so a
# more complex check is necessary.
$LogParam = if ($LogFilePath.IsPresent) {
if ($log = Resolve-Path $LogFilePath) { '/LOG+:"{0}"' -f $log }
else { '/LOG+:"{0}"' -f (New-Item -Path $LogFilePath -ItemType File -Force ) }
}
$CommandTemplate = 'robocopy.exe {0} {1} {2} {3} {4} {5} {6} {7}'
$CommandString = $CommandTemplate -f $SourceParam, $DestinationParam, $recursiveParam, $MoveParam, $MinAgeParam, $ExcludedDirParam , $NoProgressParam, $LogParam
# Use Invoke-Expression instead of Invoke-Command for much easier management
# Collect the output in a variable, so we can decide if output it or not
$CommandOutput = Invoke-Expression $CommandString
if (-not $Silent.IsPresent) { $CommandOutput }
# ?????????????
# PROFIT!
}
# And now for testing!
$LogFilePath = 'c:\temp\logs\invoke-robocopy.log'
$usrdisk = 'TESTGROUND011'
$transln = 'TransFolder'
$ExcludedDirectories = "$usrdisk\$transln\Finance", "$usrdisk\$transln\Logistic", "$usrdisk\$transln\Pricing", "$usrdisk\$transln\StreamServe"
Invoke-Robocopy "$usrdisk\$transln" "$usrdisk\$transln\delete" -Move -NoProgress -ExcludeDirectories $ExcludedDirectories -Recursive -LogFilePath $LogFilePath