I wrote Windows PowerShell Cmdlet (Advanced Function) to work with Robocopy.
- It is possible to use ComputerName (invoke all operations) or Session (use established session) to copy items on a remote computer.
- Results are not displayed as text but outputted as object. Some of the properties:
- Directories / Files / Bytes Total
- Directories / Files / Bytes Copied
- Directories / Files / Bytes Skipped
- Directories / Files / Bytes Mismatch
- Directories / Files / Bytes Failed
- Directories / Files / Bytes Extras
- Similar behavior as Copy-Item. It is possible to specify Container parameter to copy whole directory or its content.
- It is possible to copy files and not only whole directories.
- It is possible to copy multiple files and directories.
- It is possible to pipe System.IO.DirectoryInfo (directory object) or System.IO.FileInfo (file object). That is mean that is possible to pipe for example output from Get-Item or Get-ChildItem.
- Error handling that ensure that any errors (filed copies) are displayed as error.
Examples
Copy whole directory to destination – Similar to Copy-Item
Copy-RvItemRobocopy ` -Path 'C:\Temp\Source' ` -Destination 'C:\Temp\Target' ` -LogDirectoryPath 'C:\Temp'
Same as previous with usage of pipeline
Get-ChildItem -Path 'C:\Temp' -Filter S* -Directory | Copy-RvItemRobocopy ` -Destination 'C:\Temp\Target' ` -LogDirectoryPath 'C:\Temp'
Copy content of a directory – Same behaviour as Robocopy
Copy-RvItemRobocopy ` -Path 'C:\Temp\Source' ` -Destination 'C:\Temp\Target' ` -Container:$false ` -LogDirectoryPath 'C:\Temp'
Same as previous with usage of pipeline
Get-ChildItem -Path 'C:\Temp' -Filter S* -Directory | Copy-RvItemRobocopy ` -Destination 'C:\Temp\Target' ` -Container:$true ` -LogDirectoryPath 'C:\Temp'
Copy multiple files
Copy-RvItemRobocopy ` -Path 'C:\Temp\Source\1.txt', 'C:\Temp\Source\2.txt', 'C:\Temp\Source\3.txt' ` -Destination 'C:\Temp\Target' ` -LogDirectoryPath 'C:\Temp'
Copy multiple files using pipeline
Get-ChildItem -Path 'C:\Temp\Source' -Filter *.txt | Copy-RvItemRobocopy ` -Destination 'C:\Temp\Target' ` -LogDirectoryPath 'C:\Temp'
Code
Function Copy-RvItemRobocopy { <# .SYNOPSIS Copy files and directories using Robocopy. .DESCRIPTION Developer Developer: Rudolf Vesely, http://rudolfvesely.com/ Copyright (c) Rudolf Vesely. All rights reserved License: Free for private use only "RV" are initials of the developer's name Rudolf Vesely and distingue names of Rudolf Vesely's cmdlets from the other cmdlets. Description Copy files and directories using Robocopy. Similar behaviour as Copy-Item -Recurse:$true (always recursive copy of directories) -Container:$true (by default True but could be defined) # Copy whole directory (not just content) - Create D:\Temp\Target\Source with all child files and directories from D:\Temp\Source # Copy-Item -Path D:\Temp\Source -Destination D:\Temp\Target -Recurse:$true # Same as previous # Copy-Item -Path D:\Temp\Source -Destination D:\Temp\Target -Recurse:$true -Container:$true # Copy content of the directory - Create D:\Temp\Target with all child files and directories from D:\Temp\Source # Copy-Item -Path D:\Temp\Source -Destination D:\Temp\Target -Recurse:$true -Container:$false Requirements Developed and tested using PowerShell 4.0. .PARAMETER Path Source path. .PARAMETER Destination Path of the destination directory. .PARAMETER Container Preserves container objects during the copy operation. Possibilities $true D:\Temp\Source -> D:\Temp\Target = D:\Temp\Target\Source $false D:\Temp\Source -> D:\Temp\Target = D:\Temp\Target .PARAMETER ExcludedFile List of excluded files. It is possible to pass full path (C:\Temp\myfile.bin), file name (myfile.bin) or wildcard (*.bin). .PARAMETER ExcludedFile List of excluded directories. It is possible to pass full path (C:\Temp\MyDir) or directory name (MyDir). .PARAMETER Process Copy or Mirror .PARAMETER Information Alternate data streams that should be copied (only date and times of creation and modifications or everything including attibutes and ACLs). .PARAMETER Configuration List (string array) of specific configuration options. Possibilities VolumeRoot Exclude directories that should not be copied: $RECYCLE.BIN and System Volume Information .PARAMETER LogPath Full path of robocopy log that will be created. .PARAMETER LogDirectoryPath Path of a directory where Robocpy log will be created. .PARAMETER LogFileName File name for Robocopy. LogDirectoryPath is specified and file name is not specified then file name will be generated. .PARAMETER ComputerName Invoke all operations on a remote computer. .PARAMETER ComputerName Do all operations on a remote computer and use already established session. .EXAMPLE 'Copy whole directory to destination - Similar to Copy-Item' Copy-RvItemRobocopy ` -Path 'D:\Temp\Source' ` -Destination 'D:\Temp\Target' ` -LogDirectoryPath 'D:\Temp' -Debug .EXAMPLE 'Same as previous with usage of pipeline' Get-ChildItem -Path 'D:\Temp' -Filter S* -Directory | Copy-RvItemRobocopy ` -Destination 'D:\Temp\Target' ` -LogDirectoryPath 'D:\Temp' -Debug .EXAMPLE 'Copy content of a directory - Same behaviour as Robocopy' Copy-RvItemRobocopy ` -Path 'D:\Temp\Source' ` -Destination 'D:\Temp\Target' ` -Container:$false ` -LogDirectoryPath 'D:\Temp' -Debug .EXAMPLE 'Same as previous with usage of pipeline' Get-ChildItem -Path 'D:\Temp' -Filter S* -Directory | Copy-RvItemRobocopy ` -Destination 'D:\Temp\Target' ` -Container:$true ` -LogDirectoryPath 'D:\Temp' -Debug .EXAMPLE 'Copy multiple files' Copy-RvItemRobocopy ` -Path 'D:\Temp\Source\1.txt', 'D:\Temp\Source\2.txt', 'D:\Temp\Source\3.txt' ` -Destination 'D:\Temp\Target' ` -LogDirectoryPath 'D:\Temp' -Debug .EXAMPLE 'Copy multiple files using pipeline' Get-ChildItem -Path 'D:\Temp\Source' -Filter *.txt | Copy-RvItemRobocopy ` -Destination 'D:\Temp\Target' ` -LogDirectoryPath 'D:\Temp' .INPUTS System.IO.DirectoryInfo (directory object) or System.IO.FileInfo (file object) .OUTPUTS System.Management.Automation.PSCustomObject .LINK https://techstronghold.com/ #> [CmdletBinding( DefaultParametersetName = 'ComputerName', SupportsShouldProcess = $true, PositionalBinding = $false, HelpURI = 'https://techstronghold.com/', ConfirmImpact = 'Medium' )] Param ( [Parameter( Mandatory = $true, Position = 0, # ParameterSetName = '', ValueFromPipelineByPropertyName = $true )] [ValidateLength(1, 1024)] [Alias('FullName')] [string[]]$Path, [Parameter( Mandatory = $true, Position = 1 # ParameterSetName = '' )] [ValidateLength(1, 1024)] [Alias('DestinationPath')] [string]$Destination, [Parameter( Mandatory = $false # Position = , # ParameterSetName = '' )] [bool]$Container = $true, [Parameter( Mandatory = $false # Position = , # ParameterSetName = '' )] [ValidateLength(1, 1024)] [string[]]$ExcludedFile, [Parameter( Mandatory = $false # Position = , # ParameterSetName = '' )] [ValidateLength(1, 1024)] [string[]]$ExcludedDirectory, [Parameter( Mandatory = $false # Position = , # ParameterSetName = '' )] [ValidateSet( 'Copy', 'Mirror' )] [string]$Process = 'Copy', [Parameter( Mandatory = $false # Position = , # ParameterSetName = '' )] [ValidateSet( 'All', 'DateAndTime' )] [string]$Information = 'DateAndTime', [Parameter( Mandatory = $false # Position = , # ParameterSetName = '' )] [ValidateSet( 'VolumeRoot' )] [AllowNull()] [string[]]$Configuration, [Parameter( Mandatory = $false # Position = , # ParameterSetName = '' )] [AllowNull()] [AllowEmptyString()] [ValidateLength(0, 255)] [string]$LogPath, [Parameter( Mandatory = $false # Position = , # ParameterSetName = '' )] [AllowNull()] [AllowEmptyString()] [ValidateLength(0, 255)] [string]$LogDirectoryPath, [Parameter( Mandatory = $false # Position = , # ParameterSetName = '' )] [AllowNull()] [AllowEmptyString()] [ValidateLength(0, 255)] [string]$LogFileName, [Parameter( Mandatory = $false, # Position = , ParameterSetName = 'ComputerName' )] [AllowNull()] [ValidateLength(1, 255)] [string[]]$ComputerName = '.', [Parameter( Mandatory = $true, # Position = , ParameterSetName = 'Session' )] [AllowNull()] [System.Management.Automation.Runspaces.PSSession[]]$Session ) Begin { $ErrorActionPreference = 'Stop' if ($PSBoundParameters['Debug']) { $DebugPreference = 'Continue' } Set-PSDebug -Strict Set-StrictMode -Version Latest $logDateTimeUtc = Get-Date -Format yyyyMMddHHmmss # Paths if ($LogPath) { $logPathFullItem = $LogPath } elseif ($LogDirectoryPath) { if ($LogFileName) { $logPathFullItem = Join-Path -Path $LogDirectoryPath -ChildPath $LogFileName } else { $logPathFullItem = Join-Path -Path $LogDirectoryPath -ChildPath ('{0}.log' -f $logDateTimeUtc) } } else { $logPathFullItem = $null } if ($logPathFullItem) { New-RvDirectory -Path (Split-Path -Path $logPathFullItem) } #region Functions Function Copy-RvItemRobocopyProcess { [CmdletBinding( DefaultParametersetName = 'Path', SupportsShouldProcess = $true, PositionalBinding = $true, HelpURI = 'https://techstronghold.com/', ConfirmImpact = 'Medium' )] Param ( [Parameter( Mandatory = $true, # Position = , ParameterSetName = 'Path' )] [string[]]$Path, [Parameter( Mandatory = $true # Position = , # ParameterSetName = '' )] [string]$Destination, [Parameter( Mandatory = $true # Position = , # ParameterSetName = '' )] [bool]$Container, [Parameter( Mandatory = $true # Position = , # ParameterSetName = '' )] [AllowNull()] [string[]]$ExcludedFile, [Parameter( Mandatory = $true # Position = , # ParameterSetName = '' )] [AllowNull()] [string[]]$ExcludedDirectory, [Parameter( Mandatory = $true # Position = , # ParameterSetName = '' )] [string]$Process, [Parameter( Mandatory = $true # Position = , # ParameterSetName = '' )] [string]$Information, [Parameter( Mandatory = $true # Position = , # ParameterSetName = '' )] [AllowNull()] [string[]]$Configuration, [Parameter( Mandatory = $true # Position = , # ParameterSetName = '' )] [AllowNull()] [AllowEmptyString()] [string]$LogPath ) Begin { <# Special configuration #> if ($Configuration -contains 'VolumeRoot') { $ExcludedDirectory += '$RECYCLE.BIN', 'System Volume Information' } } Process { foreach ($pathItem in $Path) { <# Flags #> $resultStatus = $true $resultStatusDescription = @() $resultError = $false $resultErrorDescription = @() <# Source and destination #> Write-Debug -Message ' - Items' Write-Debug -Message (' - Source : {0}' -f $pathItem) # Wrong path $item = Get-Item -Path $pathItem -ErrorAction SilentlyContinue if (!$item) { Write-Error -Message ('Path is wrong or you do not have permissions: {0}' -f $pathItem) } # Copy directory elseif ($item.PSIsContainer) { Write-Debug -Message ' - Type: Directory' if ($Container) { $destinationItem = Join-Path -Path $Destination -ChildPath (Split-Path -Path $pathItem -Leaf) } else { $destinationItem = $Destination } $parametersAndArguments = $pathItem, $destinationItem } # Copy file else { Write-Debug -Message ' - Type: File' $destinationItem = $Destination $parametersAndArguments = (Split-Path -Path $pathItem -Parent), $destinationItem, $item.Name } Write-Debug -Message (' - Destination : {0}' -f $destinationItem) <# Parameters and arguments #> # Information if ($Information -eq 'All') { $parametersAndArguments += '/COPYALL', '/DCOPY:T' } else # if ($Information -eq 'DateAndTime') { $parametersAndArguments += '/COPY:DT', '/DCOPY:T' } # Process if ($Process -eq 'Copy') { if ($item.PSIsContainer) { $parametersAndArguments += '/E' } } else # if ($Process -eq 'Mirror') { $parametersAndArguments += '/MIR' } # Common $parametersAndArguments += '/R:0', '/V' # Log if ($logPathFullItem) { $parametersAndArguments += ('/UNILOG+:{0}' -f $logPathFullItem), '/NP', '/TS', '/TEE' } # Exclusions if ($ExcludedFile) { $parametersAndArguments += '/XF', $ExcludedFile } if ($ExcludedDirectory) { $parametersAndArguments += '/XF', $ExcludedDirectory } <# Process #> Write-Debug -Message (' - & ''robocopy'' ''{0}''' -f ($parametersAndArguments -join ''', ''')) $outputRaw = & 'robocopy' $parametersAndArguments # Decrease amount of data that will be further processed $outputRawFooter = $outputRaw | Select-Object -Last 35 $outputRawResultTable = $outputRaw | Select-Object -Last 14 $outputTextFooter = $outputRawFooter -join "`r`n" Write-Debug -Message $outputTextFooter <# Exit code #> $resultExitCode = $LASTEXITCODE if ($resultExitCode -eq 0 -or $resultExitCode -eq 1) { Write-Debug -Message ' - Results: True' } else { Write-Debug -Message ' - Results: False' Write-Warning -Message ('Robocopy: Results: False; Last exit code: {0}' -f $resultExitCode) $resultStatus = $false $resultError = $true $resultErrorDescription += 'Exit code: {0}' -f $resultExitCode } switch ($resultExitCode) { 0 { $resultExitCodeDescription = 'No errors occurred, and no copying was done. The source and destination directory trees are completely synchronized.'; break } 1 { $resultExitCodeDescription = 'One or more files were copied successfully (that is, new files have arrived).'; break } 2 { $resultExitCodeDescription = 'Some Extra files or directories were detected. No files were copied'; break } 3 { $resultExitCodeDescription = 'Some files were copied. Additional files were present. No failure was encountered.'; break } 4 { $resultExitCodeDescription = 'Some Mismatched files or directories were detected.'; break } 5 { $resultExitCodeDescription = 'Some files were copied. Some files were mismatched. No failure was encountered.'; break } 6 { $resultExitCodeDescription = 'Additional files and mismatched files exist. No files were copied and no failures were encountered. This means that the files already exist in the destination directory'; break } 7 { $resultExitCodeDescription = 'Files were copied, a file mismatch was present, and additional files were present.'; break } 8 { $resultExitCodeDescription = 'Some files or directories could not be copied (copy errors occurred and the retry limit was exceeded).'; break } 16 { $resultExitCodeDescription = 'Serious error. Robocopy did not copy any files. Either a usage error or an error due to insufficient access privileges on the source or destination directories.'; break } Default { $resultExitCodeDescription = 'Unknown exit code' } } <# Results #> # 0, 2439043 $resultDirectories = ((($outputRawResultTable -match '^\s+Dirs :\s+\d') -replace '.*:\s+', '').Trim()) -split '\s+' $resultFiles = ((($outputRawResultTable -match '^\s+Files :\s+\d') -replace '.*:\s+', '').Trim()) -split '\s+' # 239.49 m, 82.497 g $resultBytes = ((($outputRawResultTable -match '^\s+Bytes :\s+\d') -replace '.*:\s+', '').Trim()) -split '\s{2,}' $resultErrorNumbersInTable = $false try { $resultDirectoriesTotal = [Int64]$resultDirectories[0] } catch { $resultErrorNumbersInTable = $true; $resultDirectoriesTotal = 'Error' } try { $resultDirectoriesCopied = [Int64]$resultDirectories[1] } catch { $resultErrorNumbersInTable = $true; $resultDirectoriesCopied = 'Error' } try { $resultDirectoriesSkipped = [Int64]$resultDirectories[2] } catch { $resultErrorNumbersInTable = $true; $resultDirectoriesSkipped = 'Error' } try { $resultDirectoriesMismatch = [Int64]$resultDirectories[3] } catch { $resultErrorNumbersInTable = $true; $resultDirectoriesMismatch = 'Error' } try { $resultDirectoriesFailed = [Int64]$resultDirectories[4] } catch { $resultErrorNumbersInTable = $true; $resultDirectoriesFailed = 'Error' } try { $resultDirectoriesExtras = [Int64]$resultDirectories[5] } catch { $resultErrorNumbersInTable = $true; $resultDirectoriesExtras = 'Error' } try { $resultFilesTotal = [Int64]$resultFiles[0] } catch { $resultErrorNumbersInTable = $true; $resultFilesTotal = 'Error' } try { $resultFilesCopied = [Int64]$resultFiles[1] } catch { $resultErrorNumbersInTable = $true; $resultFilesCopied = 'Error' } try { $resultFilesSkipped = [Int64]$resultFiles[2] } catch { $resultErrorNumbersInTable = $true; $resultFilesSkipped = 'Error' } try { $resultFilesMismatch = [Int64]$resultFiles[3] } catch { $resultErrorNumbersInTable = $true; $resultFilesMismatch = 'Error' } try { $resultFilesFailed = [Int64]$resultFiles[4] } catch { $resultErrorNumbersInTable = $true; $resultFilesFailed = 'Error' } try { $resultFilesExtras = [Int64]$resultFiles[5] } catch { $resultErrorNumbersInTable = $true; $resultFilesExtras = 'Error' } $resultBytesTotal = $resultBytes[0] $resultBytesCopied = $resultBytes[1] $resultBytesSkipped = $resultBytes[2] $resultBytesMismatch = $resultBytes[3] $resultBytesFailed = $resultBytes[4] $resultBytesExtras = $resultBytes[5] if ($resultErrorNumbersInTable) { $resultStatus = $false $resultError = $true $resultErrorDescription += 'Cannot gather results' } if ($resultDirectoriesFailed -gt 0 -or $resultFilesFailed -gt 0) { $resultStatus = $false $resultError = $true $resultErrorDescription += 'Some of the copies failed' } # Return [PsCustomObject]@{ Source = $pathItem Destination = $destinationItem ItemType = $(if ($item.PSIsContainer) { 'Container' } else { 'Leaf' }) Status = $resultStatus StatusDescription = ($resultStatusDescription -join '; ') Error = $resultError ErrorDescription = ($resultErrorDescription -join '; ') ExitCode = $resultExitCode ExitCodeDescription = $resultExitCodeDescription RobocopyLogFooter = $outputRawFooter RobocopyLogResultTable = $outputRawResultTable DirectoriesTotal = $resultDirectoriesTotal DirectoriesCopied = $resultDirectoriesCopied DirectoriesSkipped = $resultDirectoriesSkipped DirectoriesMismatch = $resultDirectoriesMismatch DirectoriesFailed = $resultDirectoriesFailed DirectoriesExtras = $resultDirectoriesExtras FilesTotal = $resultFilesTotal FilesCopied = $resultFilesCopied FilesSkipped = $resultFilesSkipped FilesMismatch = $resultFilesMismatch FilesFailed = $resultFilesFailed FilesExtras = $resultFilesExtras BytesTotal = $resultBytesTotal BytesCopied = $resultBytesCopied BytesSkipped = $resultBytesSkipped BytesMismatch = $resultBytesMismatch BytesFailed = $resultBytesFailed BytesExtras = $resultBytesExtras } if ($resultError) { Write-Error -Message ($resultErrorDescription -join '; ') } } } End { } } #endregion } Process { if ($PSCmdlet.ParameterSetName -eq 'ComputerName' -or $Session -eq $null) { foreach ($computerNameItem in $ComputerName) { # Local device if (!$computerNameItem -or $computerNameItem -eq '.' -or $computerNameItem -eq $env:COMPUTERNAME) { Copy-RvItemRobocopyProcess ` -Path $Path ` -Destination $Destination ` -Container $Container ` -ExcludedFile $ExcludedFile ` -ExcludedDirectory $ExcludedDirectory ` -Process $Process ` -Information $Information ` -Configuration $Configuration ` -LogPath $logPathFullItem } # Remote device else { Invoke-Command ` -ComputerName $computerNameItem ` -ArgumentList $Path, $Destination, $Container, $ExcludedFile, $ExcludedDirectory, $Process, $Information, $Configuration, $logPathFullItem ` -ScriptBlock ${Function:Copy-RvItemRobocopyProcess} } } } else # if ($PSCmdlet.ParameterSetName -eq 'Session') { foreach ($sessionItem in $Session) { Invoke-Command ` -Session $sessionItem ` -ArgumentList $Path, $Destination, $Container, $ExcludedFile, $ExcludedDirectory, $Process, $Information, $Configuration, $logPathFullItem ` -ScriptBlock ${Function:Copy-RvItemRobocopyProcess} } } } End { } }
2 responses to “PowerShell advanced function (cmdlet) to copy files and folders using Robocopy on a local or remote computer”
"The term ‘New-RvDirectory’ is not recognized as the name of a cmdlet, function, script file, or operable program."
Based on the code I am seeing the script appears to be a very nice way to setup RoboCopy to run in an environment. However, I am getting the same error when using this script on the ‘New-RVDirectory’ but assuming you are looking to create a new directory that is an easy fix. However the code appears to have an additional issue with the ‘Copy-RvItemRobocopyProcess’ command. I am getting an error that the system object does not contain a method named trim. It appears that on the function for ‘Copy-RvItemRobocopyProcess’ the parameters you are using are treating the string as a folder object and it cannot pass through your function without throwing up errors.