Functions/GenXdev.FileSystem/Start-RoboCopy.ps1
############################################################################### <# .SYNOPSIS Wrapper for Microsoft's Robust Copy Utility Copies file data from one location to another. .DESCRIPTION Wrapper for Microsoft's Robust Copy Utility Copies file data from one location to another. Robocopy, for "Robust File Copy", is a command-line directory and/or file replication command for Microsoft Windows. Robocopy functionally replaces Xcopy, with more options. Created by Kevin Allen and first released as part of the Windows NT 4.0 Resource Kit, it has been a standard feature of Windows since Windows Vista and Windows Server 2008. Key features - Folder synchronization - Support for extra long pathnames > 256 characters - Restartable mode backups - Support for copying and fixing security settings - Advanced file attribute features - Advanced symbolic link and junction support - Monitor mode (restart copying after change threshold) - Optimization features for LargeFiles, multithreaded copying and network compression - Recovery mode (copy from failing disks) .PARAMETER Source The directory, filepath, or directory+searchmask .PARAMETER DestinationDirectory The destination directory to place the copied files and directories into. If this directory does not exist yet, all missing directories will be created. Default value = `.\` .PARAMETER FileMask Optional searchmask for selecting the files that need to be copied. .PARAMETER Mirror Synchronizes the content of specified directories, will also delete any files and directories in the destination that do not exist in the source .PARAMETER Move Will move instead of copy all files from source to destination .PARAMETER IncludeSecurity Will also copy ownership, security descriptors and auditing information of files and directories .PARAMETER SkipDirectories Copies only files from source and skips sub-directories (no recurse) .PARAMETER SkipEmptyDirectories Does not copy directories if they would be empty .PARAMETER CopyOnlyDirectoryTreeStructure Create directory tree only .PARAMETER CopyOnlyDirectoryTreeStructureAndEmptyFiles Create directory tree and zero-length files only .PARAMETER SkipAllSymbolicLinks Don't copy symbolic links, junctions or the content they point to .PARAMETER CopySymbolicLinksAsLinks Instead of copying the content where symbolic links point to, copy the links themselves .PARAMETER SkipJunctions Don't copy directory junctions (symbolic link for a folder) or the content they point to .PARAMETER SkipSymbolicFileLinks Don't copy file symbolic links but do follow directory junctions .PARAMETER CopyJunctionsAsJunctons Instead of copying the content where junctions point to, copy the junctions themselves .PARAMETER Force Will copy all files even if they are older then the ones in the destination .PARAMETER SkipFilesWithoutArchiveAttribute Copies only files that have the archive attribute set .PARAMETER ResetArchiveAttributeAfterSelection In addition of copying only files that have the archive attribute set, will then reset this attribute on the source .PARAMETER FileExcludeFilter Exclude any files that matches any of these names/paths/wildcards .PARAMETER DirectoryExcludeFilter Exclude any directories that matches any of these names/paths/wildcards .PARAMETER AttributeIncludeFilter Copy only files that have all these attributes set [RASHCNETO] .PARAMETER AttributeExcludeFilter Exclude files that have any of these attributes set [RASHCNETO] .PARAMETER SetAttributesAfterCopy Will set the given attributes to copied files [RASHCNETO] .PARAMETER RemoveAttributesAfterCopy Will remove the given attributes from copied files [RASHCNETO] .PARAMETER MaxSubDirTreeLevelDepth Only copy the top n levels of the source directory tree .PARAMETER MinFileSize Skip files that are not at least n bytes in size .PARAMETER MaxFileSize Skip files that are larger then n bytes .PARAMETER MinFileAge Skip files that are not at least: n days old OR created before n date (if n < 1900 then n = n days, else n = YYYYMMDD date) .PARAMETER MaxFileAge Skip files that are older then: n days OR created after n date (if n < 1900 then n = n days, else n = YYYYMMDD date) .PARAMETER MinLastAccessAge Skip files that are accessed within the last: n days OR before n date (if n < 1900 then n = n days, else n = YYYYMMDD date) .PARAMETER MaxLastAccessAge Skip files that have not been accessed in: n days OR after n date (if n < 1900 then n = n days, else n = YYYYMMDD date) .PARAMETER RecoveryMode Will shortly pause and retry when I/O errors occur during copying .PARAMETER MonitorMode Will stay active after copying, and copy additional changes after a a default threshold of 10 minutes .PARAMETER MonitorModeThresholdMinutes Run again in n minutes Time, if changed .PARAMETER MonitorModeThresholdNrOfChanges Run again when more then n changes seen .PARAMETER MonitorModeRunHoursFrom Run hours - times when new copies may be started, start-time, range 0000:2359 .PARAMETER MonitorModeRunHoursUntil Run hours - times when new copies may be started, end-time, range 0000:2359 .PARAMETER LogFilePath If specified, logging will also be done to specified file .PARAMETER LogfileOverwrite Don't append to the specified logfile, but overwrite instead .PARAMETER LogDirectoryNames Include all scanned directory names in output .PARAMETER LogAllFileNames Include all scanned file names in output, even skipped onces .PARAMETER Unicode Output status as UNICODE .PARAMETER LargeFiles Enables optimization for copying large files .PARAMETER Multithreaded Optimize performance by doing multithreaded copying .PARAMETER CompressibleContent If applicable use compression when copying files between servers to safe bandwidth and time .PARAMETER Override Overrides, Removes, or Adds any specified robocopy parameter. Usage: Add or replace parameter: -Override /SwitchWithValue:'SomeValue' -Override /Switch Remove parameter: -Override -/Switch Multiple overrides: -Override "/ReplaceThisSwitchWithValue:'SomeValue' -/RemoveThisSwitch /AddThisSwitch" .PARAMETER WhatIf Displays a message that describes the effect of the command, instead of executing the command. .EXAMPLE Start-RoboCopy c:\videos e:\backups\videos Start-RoboCopy c:\users\user\onedrive\photos\screenshots e:\backups\screenshots -Move Start-RoboCopy c:\users\user\onedrive e:\backups\onedrive -Mirror .LINK https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/robocopy .LINK https://en.wikipedia.org/wiki/Robocopy #> function Start-RoboCopy { [CmdLetBinding( DefaultParameterSetName = "Default", ConfirmImpact = "Medium" )] [Alias("xc", "rc")] Param ( ############################################################################### [Parameter( Mandatory, Position = 0, ValueFromPipeline = $false, HelpMessage = "The directory, filepath, or directory+searchmask" )] [string]$Source, ############################################################################### [Parameter( Mandatory = $false, Position = 1, ValueFromPipeline = $false, HelpMessage = "The destination directory to place the copied files and directories into. If this directory does not exist yet, all missing directories will be created. Default value = `".\`"" )] [string]$DestinationDirectory = ".$([System.IO.Path]::DirectorySeparatorChar)", ############################################################################### [Parameter( Mandatory = $false, ValueFromPipeline = $false, Position = 2, HelpMessage = "Optional searchmask for selecting the files that need to be copied. Default value = '*'" )] [string[]] $Files = @(), ############################################################################### ############################################################################### [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Synchronizes the content of specified directories, will also delete any files and directories in the destination that do not exist in the source" )] [switch] $Mirror, ############################################################################### [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Will move instead of copy all files from source to destination" )] [switch] $Move, ############################################################################### [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Will also copy ownership, security descriptors and auditing information of files and directories" )] [switch] $IncludeSecurity, ############################################################################### ############################################################################### [Parameter( ParameterSetName = "Default", Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Copies only files from source and skips sub-directories (no recurse)" )] [switch] $SkipDirectories, ############################################################################### [Parameter( ParameterSetName = "SkipDirectories", Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Does not copy directories if they would be empty" )] [switch] $SkipEmptyDirectories, ############################################################################### [Parameter( ParameterSetName = "SkipDirectories", Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Create directory tree only" )] [switch] $CopyOnlyDirectoryTreeStructure, ############################################################################### [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Create directory tree and zero-length files only" )] [switch] $CopyOnlyDirectoryTreeStructureAndEmptyFiles, ############################################################################### ############################################################################### [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Don't copy symbolic links, junctions or the content they point to" )] [switch] $SkipAllSymbolicLinks, ############################################################################### [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Don't copy file symbolic links but do follow directory junctions" )] [switch] $SkipSymbolicFileLinks, ############################################################################### [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Instead of copying the content where symbolic links point to, copy the links themselves" )] [switch] $CopySymbolicLinksAsLinks, ############################################################################### [Parameter( ParameterSetName = "SkipDirectories", Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Don't copy directory junctions (symbolic link for a folder) or the content they point to" )] [switch] $SkipJunctions, ############################################################################### [Parameter( ParameterSetName = "SkipDirectories", Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Instead of copying the content where junctions point to, copy the junctions themselves" )] [switch] $CopyJunctionsAsJunctons, ############################################################################### ############################################################################### [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Will copy all files even if they are older then the ones in the destination" )] [switch] $Force, ############################################################################### ############################################################################### [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Copies only files that have the archive attribute set" )] [switch] $SkipFilesWithoutArchiveAttribute, ############################################################################### [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "In addition of copying only files that have the archive attribute set, will then reset this attribute on the source" )] [switch] $ResetArchiveAttributeAfterSelection, ############################################################################### [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Exclude any files that matches any of these names/paths/wildcards" )] [string[]] $FileExcludeFilter = @(), ############################################################################### [Parameter( ParameterSetName = "SkipDirectories", Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Exclude any directories that matches any of these names/paths/wildcards" )] [string[]] $DirectoryExcludeFilter = @(), ############################################################################### [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Copy only files that have all these attributes set [RASHCNETO]" )] [string] $AttributeIncludeFilter, ############################################################################### [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Exclude files that have any of these attributes set [RASHCNETO]" )] [string] $AttributeExcludeFilter, ############################################################################### [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Will set the given attributes to copied files [RASHCNETO]" )] [string] $SetAttributesAfterCopy, ############################################################################### [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Will remove the given attributes from copied files [RASHCNETO]" )] [string] $RemoveAttributesAfterCopy, ############################################################################### ############################################################################### [ValidateRange(1, 1000000)] [Parameter( ParameterSetName = "SkipDirectories", Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Only copy the top n levels of the source directory tree" )] [int] $MaxSubDirTreeLevelDepth = -1, ############################################################################### [ValidateRange(0, 9999999999999999)] [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Skip files that are not at least n bytes in size" )] [int] $MinFileSize = -1, ############################################################################### [ValidateRange(0, 9999999999999999)] [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Skip files that are larger then n bytes" )] [int] $MaxFileSize = -1, ############################################################################### [ValidateRange(0, 99999999)] [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Skip files that are not at least: n days old OR created before n date (if n < 1900 then n = n days, else n = YYYYMMDD date)" )] [int] $MinFileAge = -1, ############################################################################### [ValidateRange(0, 99999999)] [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Skip files that are older then: n days OR created after n date (if n < 1900 then n = n days, else n = YYYYMMDD date)" )] [int] $MaxFileAge = -1, ############################################################################### [ValidateRange(0, 99999999)] [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Skip files that are accessed within the last: n days OR before n date (if n < 1900 then n = n days, else n = YYYYMMDD date)" )] [int] $MinLastAccessAge = -1, ############################################################################### [ValidateRange(0, 99999999)] [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Skip files that have not been accessed in: n days OR after n date (if n < 1900 then n = n days, else n = YYYYMMDD date)" )] [int] $MaxLastAccessAge = -1, ############################################################################### ############################################################################### [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Will shortly pause and retry when I/O errors occur during copying" )] [switch] $RecoveryMode, ############################################################################### ############################################################################### [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Will stay active after copying, and copy additional changes after a a default threshold of 10 minutes" )] [switch] $MonitorMode, ############################################################################### [ValidateRange(1, 144000)] [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Run again in n minutes Time, if changed" )] [int] $MonitorModeThresholdMinutes = -1, ############################################################################### [ValidateRange(1, 1000000000)] [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Run again when more then n changes seen" )] [int] $MonitorModeThresholdNrOfChanges = -1, ############################################################################### [ValidateRange(0, 2359)] [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Run hours - times when new copies may be started, start-time, range 0000:2359" )] [int] $MonitorModeRunHoursFrom = -1, ############################################################################### [ValidateRange(0, 2359)] [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Run hours - times when new copies may be started, end-time, range 0000:2359" )] [int] $MonitorModeRunHoursUntil = -1, ############################################################################### ############################################################################### [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "If specified, logging will also be done to specified file" )] [string] $LogFilePath, ############################################################################### [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Don't append to the specified logfile, but overwrite instead" )] [switch] $LogfileOverwrite, ############################################################################### [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Include all scanned directory names in output" )] [switch] $LogDirectoryNames, ############################################################################### [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Include all scanned file names in output, even skipped onces" )] [switch] $LogAllFileNames, ############################################################################### [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Output status as UNICODE" )] [switch] $Unicode, ############################################################################### ############################################################################### [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Enables optimization for copying large files" )] [switch] $LargeFiles, ############################################################################### [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Optimize performance by doing multithreaded copying" )] [switch] $MultiThreaded, ############################################################################### [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "If applicable use compression when copying files between servers to safe bandwidth and time" )] [switch] $CompressibleContent, ############################################################################### [Parameter( Mandatory = $false, ValueFromPipeline = $false, ValueFromRemainingArguments, Position = 3, HelpMessage = "Overrides, Removes, or Adds any specified robocopy parameter. Usage: Add or replace parameter: -Override /SwitchWithValue:'SomeValue' -Override /Switch Remove parameter: -Override -/Switch Multiple overrides: -Override `"/ReplaceThisSwitchWithValue:'SomeValue' -/RemoveThisSwitch /AddThisSwitch`" " )] [string] $Override, ############################################################################### ############################################################################### [Parameter( Mandatory = $false, ValueFromPipeline = $false, HelpMessage = "Displays a message that describes the effect of the command, instead of executing the command." )] [switch] $WhatIf ) Begin { ############################################################################### # initialize settings $RobocopyPath = "$env:SystemRoot\system32\robocopy.exe"; # normalize to current directory $Source = Expand-Path $Source $DestinationDirectory = Expand-Path $DestinationDirectory # source is not an existing directory? if ([IO.Directory]::Exists($Source) -eq $false) { # split directory and filename $SourceSearchMask = [IO.Path]::GetFileName($Source); $SourceDirOnly = [IO.Path]::GetDirectoryName($Source); # does parent directory exist? if ([IO.Directory]::Exists($SourceDirOnly)) { # ..but the supplied source parameter is not an existing file? if ([IO.File]::Exists($Source) -eq $false) { # ..and the supplied filename is not searchMask? if (!$SourceSearchMask.Contains("*") -and !$SourceSearchMask.Contains("?")) { throw "Could not find source: $Source" } } $Mirror = $false; } # reconfigure $Source = $SourceDirOnly; if ($Files -notcontains $SourceSearchMask) { $Files = $Files + @($SourceSearchMask); } } # default value if ($Files.Length -eq 0) { $Files = @("*"); } # destination directory does not exist yet? if ([IO.Directory]::Exists($DestinationDirectory) -eq $false) { # create it [IO.Directory]::CreateDirectory($DestinationDirectory) | Out-Null } # Turn on verbose $VerbosePreference = "Continue" ############################################################################### function CurrentUserHasElivatedRights() { $id = [System.Security.Principal.WindowsIdentity]::GetCurrent() $p = New-Object System.Security.Principal.WindowsPrincipal($id) if ($p.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator) -or $p.IsInRole([System.Security.Principal.WindowsBuiltInRole]::BackupOperator)) { return $true; } return $false; } function ConstructFileFilterSet([string[]] $FileFilterSet, [string] $CommandName) { $result = ""; $FileFilterSet | ForEach-Object { $result = "$result '$PSItem'".Trim() } return $result; } function SanitizeAttributeSet([string] $AttributeSet, [string] $CommandName) { $AttributeSetNew = ""; $AttributeSet.Replace("[", "").Replace("]", "").ToUpperInvariant().ToCharArray() | ForEach-Object { if (("RASHCNETO".IndexOf($PSItem) -ge 0) -and ($AttributeSetNew.IndexOf($PSItem) -lt 0)) { $AttributeSetNew = "$AttributeSet$PSItem"; } else { throw "Could not parse parameter -$CommandName $AttributeSet - '$PSItem' is not valid possible attributes to combine: [RASHCNETO] R - Read only A - Archive S - System H - Hidden C - Compressed N - Not content indexed E - Encrypted T - Temporary O - Offline " } } return $AttributeSetNew } function CheckAgeInteger([int] $AgeValue, [string] $CommandName) { if ($AgeValue -ge 1900) { [DateTime] $date; if ([DateTime]::TryParse("$MaxFileAge", [ref] $date) -eq $false) { throw "Could not parse parameter '-$CommandName $AgeValue as a valid date (if n < 1900 then n = n days, else n = YYYYMMDD date)" } } } function getSwitchesDictionary([string] $Switches) { # initialize $switchesDictionary = New-Object "System.Collections.Generic.Dictionary[String, String]"; if ([String]::IsNullOrWhiteSpace($Switches)) { return $switchesDictionary } $switchesCleaned = " $Switches "; # remove spaces while ($switchesCleaned.IndexOf(" /") -ge 0) { $switchesCleaned = $switchesCleaned.Replace(" /", " /"); } while ($switchesCleaned.IndexOf(" -/") -ge 0) { $switchesCleaned = $switchesCleaned.Replace(" -/", " -/"); } # split up $allSwitches = $switchesCleaned.Replace(" -/", " /-").Split([string[]]@(" /"), [System.StringSplitOptions]::RemoveEmptyEntries); # enumerate switches $allSwitches | ForEach-Object -ErrorAction SilentlyContinue { # add to Dictionary $switchesDictionary["$($PSItem.Trim().Split(" ")[0].Split(":" )[0].Trim().ToUpperInvariant())"] = $PSItem.Trim() } return $switchesDictionary; } function overrideAndCleanSwitches([string] $Switches) { $autoGeneratedSwitches = (getSwitchesDictionary $Switches) $overridenSwitches = (getSwitchesDictionary $Override) $newSwitches = ""; $autoGeneratedSwitches.GetEnumerator() | ForEach-Object -ErrorAction SilentlyContinue { # should NOT remove it? if (!$overridenSwitches.ContainsKey("-$($PSItem.Key)")) { # should replace it? if ($overridenSwitches.ContainsKey($PSItem.Key)) { $newSwitches += " /$($overridenSwitches[$PSItem.Key])" } else { # keep the autogenerated switch $newSwitches += " /$($PSItem.Value)" } } } $overridenSwitches.GetEnumerator() | ForEach-Object -ErrorAction SilentlyContinue { # not already processed above? if (!$PSItem.Key.StartsWith("-") -and !$autoGeneratedSwitches.ContainsKey("$($PSItem.Key)")) { # add it $newSwitches += " /$($PSItem.Value)" } } return $newSwitches.Trim(); } ############################################################################### # /B █ copy files in Backup mode. # /ZB █ use restartable mode; if access denied use Backup mode. if (CurrentUserHasElivatedRights) { $ParamMode = "/B" } else { $ParamMode = "" } # /MOV █ MOVE files AND dirs (delete from source after copying). $ParamMOV = ""; # /MIR █ MIRror a directory tree (equivalent to /E plus /PURGE). $ParamMIR = ""; # /SECFIX █ FIX file SECurity on all files, even skipped files. $ParamSECFIX = ""; # /E █ copy subdirectories, including Empty ones. # /S █ copy Subdirectories, but not empty ones. $ParamDirs = "/E" # /COPY █ what to COPY for files (default is /COPY:DAT). $ParamCOPY = "/COPY:DAT" # /XO █ eXclude Older files. $ParamXO = "/XO"; # /IM █ Include Modified files (differing change times). $ParamIM = "/IM"; # /IT █ Include Tweaked files. $ParamIT = "/IT"; # /IS █ Include Same files. $ParamIS = ""; # /EFSRAW █ copy all encrypted files in EFS RAW mode. $ParamEFSRAW = ""; # /NOOFFLOAD █ copy files without using the Windows Copy Offload mechanism. $ParamNOOFFLOAD = ""; # /R █ number of Retries on failed copies: default 1 million. $ParamR = "/R:0"; # /W █ Wait time between retries: default is 30 seconds. $ParamW = "/W:0"; # /J █ copy using unbuffered I/O (recommended for large files). $paramJ = ""; # /MT █ Do multi-threaded copies with n threads (default 8). $paramMT = ""; # /NDL █ No Directory List - don't log directory names. $ParamNDL = "/NDL"; # /X █ report all eXtra files, not just those selected. $ParamX = ""; # /V █ produce Verbose output, showing skipped files. $ParamV = ""; # /CREATE █ CREATE directory tree and zero-length files only. $ParamCREATE = ""; # /XJ █ eXclude symbolic links (for both files and directories) and Junction points. $ParamXJ = ""; # /XJD █ eXclude symbolic links for Directories and Junction points. $ParamXJD = ""; # /XJF █ eXclude symbolic links for Files. $ParamXJF = ""; # /SJ █ copy Junctions as junctions instead of as the junction targets. $ParamSJ = ""; # /SL █ copy Symbolic Links as links instead of as the link targets. $ParamSL = ""; # /A █ copy only files with the Archive attribute set. # /M █ copy only files with the Archive attribute and reset it. $ParamArchive = ""; # /XF $ParamXF = "" # █ eXclude Files matching given names/paths/wildcards. # /XD $ParamXD = "" # █ eXclude Directories matching given names/paths/wildcards. # /IA █ Include only files with any of the given Attributes set. $ParamIA = ""; # /XA █ eXclude files with any of the given Attributes set. $ParamXA = ""; # /A+ █ add the given Attributes to copied files $ParamAttrSet = ""; # /A- █ remove the given Attributes from copied files. $ParamAttrRemove = ""; # /LEV █ only copy the top n LEVels of the source directory tree. $ParamLEV = ""; # /MIN █ MINimum file size - exclude files smaller than n bytes. $ParamMIN = ""; # /MAX █ MAXimum file size - exclude files bigger than n bytes. $ParamMAX = ""; # /MINAGE █ MINimum file AGE - exclude files newer than n days/date. $ParamMINAGE = ""; # /MAXAGE █ MAXimum file AGE - exclude files older than n days/date. $ParamMaxAGE = ""; # /LOG █ output status to LOG file (overwrite existing log). # /LOG+ █ output status to LOG file (append to existing log). $ParamLOG = ""; # /TEE █ output to console window, as well as the log file. $ParamTee = ""; # /UNICODE █ output status as UNICODE. $ParamUnicode = ""; # /RH █ Run Hours - times when new copies may be started. $ParamRH = ""; # /MON █ MONitor source; run again when more than n changes seen. # /MOT █ MOnitor source; run again in m minutes Time, if changed. $ParamMON = ""; # /MAXLAD █ MAXimum Last Access Date - exclude files unused since n. $ParamMAXLAD = ""; # /MINLAD █ MINimum Last Access Date - exclude files used since n. $ParamMINLAD = ""; # /COMPRESS █ Request network compression during file transfer, if applicable. $ParamCOMPRESS = ""; ############################################################################### # -Mirror ➜ Synchronizes the content of specified directories, will also delete any files and directories in the destination that do not exist in the source if ($Mirror -eq $true) { $ParamMIR = "/MIR" # █ MIRror a directory tree (equivalent to /E plus /PURGE). } # -Move ➜ Will move instead of copy all files from source to destination if ($Move -eq $true) { $ParamMOV = "/MOV" # █ MOVE files AND dirs (delete from source after copying). } # -IncludeSecurity ➜ Will also copy ownership, security descriptors and auditing information of files and directories if ($IncludeSecurity -eq $true) { $ParamSECFIX = "/SECFIX" # █ FIX file SECurity on all files, even skipped files. $ParamCOPY = "$($ParamCOPY)SOU" # █ what to COPY for files (default is /COPY:DAT). $ParamEFSRAW = "/EFSRAW" # █ copy all encrypted files in EFS RAW mode. } # -SkipDirectories ➜ Copies only files from source and skips sub-directories (no recurse) if ($SkipDirectories -eq $true) { $ParamDirs = "" # █ copy subdirectories, including Empty ones. } else { # -SkipEmptyDirectories ➜ Does not copy directories if they would be empty if ($SkipEmptyDirectories -eq $true) { $ParamDirs = "/S" # █ copy Subdirectories, but not empty ones. } } # -CopyOnlyDirectoryTreeStructure ➜ Create directory tree only if ($CopyOnlyDirectoryTreeStructure -eq $true) { $ParamCREATE = "/CREATE"; # █ CREATE directory tree and zero-length files only. $Files = @("DontCopy4nyF1lés") # █ File(s) to copy (names/wildcards: default is "*.*") } else { # -CopyOnlyDirectoryTreeStructureAndEmptyFiles ➜ Create directory tree and zero-length files only if ($CopyOnlyDirectoryTreeStructureAndEmptyFiles -eq $true) { $ParamCREATE = "/CREATE"; # █ CREATE directory tree and zero-length files only. } } # -SkipAllSymbolicLinks ➜ Don't copy symbolic links, junctions or the content they point to if ($SkipAllSymbolicLinks -eq $true) { $ParamXJ = "/XJ"; # █ eXclude symbolic links (for both files and directories) and Junction points. } else { # -SkipSymbolicFileLinks ➜ Don't copy file symbolic links but do follow directory junctions if ($SkipSymbolicFileLinks -eq $true) { $ParamXJF = "/XJF"; # █ eXclude symbolic links for Files. } else { # -CopySymbolicLinksAsLinks ➜ Instead of copying the content where symbolic links point to, copy the links themselves if ($CopySymbolicLinksAsLinks -eq $true) { $ParamSL = "/SL"; # █ copy Symbolic Links as links instead of as the link targets. } } # -SkipJunctions ➜ Don't copy directory junctions (symbolic link for a folder) or the content they point to if ($SkipJunctions -eq $true) { $ParamXJD = "/XJD"; # █ eXclude symbolic links for Directories and Junction points. } else { # -CopyJunctionsAsJunctons ➜ Instead of copying the content where junctions point to, copy the junctions themselves if ($CopyJunctionsAsJunctons -eq $true) { $ParamSJ = "/SJ"; # █ copy Junctions as junctions instead of as the junction targets. } } } ############################################################################### # -Force ➜ Will copy all files even if they are older then the ones in the destination if ($Force -eq $true) { $ParamXO = "" # █ eXclude Older files. $ParamIT = "/IT" # █ Include Tweaked files. $ParamIS = "/IS" # █ Include Same files. } ############################################################################### # -SkipFilesWithoutArchiveAttribute ➜ Copies only files that have the archive attribute set if ($SkipFilesWithoutArchiveAttribute -eq $true) { $ParamArchive = "/A" # █ copy only files with the Archive attribute set. } # -ResetArchiveAttributeAfterSelection ➜ In addition of copying only files that have the archive attribute set, will then reset this attribute on the source if ($ResetArchiveAttributeAfterSelection -eq $true) { $ParamArchive = "/M" # █ copy only files with the Archive attribute and reset it } ############################################################################### # -FileExcludeFilter ➜ Exclude any files that matches any of these names/paths/wildcards if ($FileExcludeFilter.Length -gt 0) { $Filter = "$((ConstructFileFilterSet $FileExcludeFilter "FileExcludeFilter"))"; $ParamXF = "/XF $Filter" # █ eXclude Files matching given names/paths/wildcards. } # -DirectoryExcludeFilter ➜ Exclude any directories that matches any of these names/paths/wildcards if ($DirectoryExcludeFilter.Length -gt 0) { $Filter = "$((ConstructFileFilterSet $DirectoryExcludeFilter "DirectoryExcludeFilter"))"; $ParamXD = "/XD $Filter" # █ eXclude Directories matching given names/paths/wildcards. } # -AttributeIncludeFilter ➜ Copy only files that have all these attributes set [RASHCNETO] if ([string]::IsNullOrWhiteSpace($AttributeIncludeFilter) -eq $false) { $Filter = "$((SanitizeAttributeSet $AttributeIncludeFilter "AttributeIncludeFilter"))"; $ParamIA = "/IA:$Filter" # █ Include only files with any of the given Attributes set. } # -AttributeExcludeFilter ➜ Exclude files that have any of these attributes set [RASHCNETO] if ([string]::IsNullOrWhiteSpace($AttributeExcludeFilter) -eq $false) { $Filter = "$((SanitizeAttributeSet $AttributeExcludeFilter "AttributeExcludeFilter"))"; $ParamXA = "/XA:$Filter" # █ eXclude files with any of the given Attributes set. } # -SetAttributesAfterCopy ➜ Will set the given attributes to copied files [RASHCNETO] if ([string]::IsNullOrWhiteSpace($SetAttributesAfterCopy) -eq $false) { $Filter = "$((SanitizeAttributeSet $SetAttributesAfterCopy "SetAttributesAfterCopy"))"; $ParamAttrSet = "/A+:$Filter" # █ add the given Attributes to copied files } # -RemoveAttributesAfterCopy ➜ Will remove the given attributes from copied files [RASHCNETO] if ([string]::IsNullOrWhiteSpace($RemoveAttributesAfterCopy) -eq $false) { $Filter = "$((SanitizeAttributeSet $RemoveAttributesAfterCopy "RemoveAttributesAfterCopy"))"; $ParamAttrRemove = "/A+:$Filter" # █ remove the given Attributes from copied files. } # -MaxSubDirTreeLevelDepth ➜ Only copy the top n levels of the source directory tree if ($MaxSubDirTreeLevelDepth -ge 0) { $ParamLEV = "/LEV:$MaxSubDirTreeLevelDepth" # █ only copy the top n LEVels of the source directory tree. } # -MinFileSize ➜ Skip files that are not at least n bytes in size if ($MinFileSize -ge 0) { $ParamMIN = "/MIN:$MinFileSize" # █ MINimum file size - exclude files smaller than n bytes. } # -MaxFileSize ➜ Skip files that are larger then n bytes if ($MaxFileSize -ge 0) { $ParamMAX = "/MAX:$MinFileSize" # █ MAXimum file size - exclude files bigger than n bytes. } # -MinFileAge ➜ Skip files that are not at least: n days old OR created before n date (if n < 1900 then n = n days, else n = YYYYMMDD date) if ($MinFileAge -ge 0) { CheckAgeInteger $MinFileAge "MinFileAge" $ParamMINAGE = "/MINAGE:$MinFileAge" # █ MINimum file AGE - exclude files newer than n days/date. } # -MaxFileAge ➜ Skip files that are older then: n days OR created after n date (if n < 1900 then n = n days, else n = YYYYMMDD date) if ($MaxFileAge -ge 0) { CheckAgeInteger $MaxFileAge "MaxFileAge" $ParamMaxAGE = "/MAXAGE:$MaxFileAge" # █ MAXimum file AGE - exclude files older than n days/date. } # -MinLastAccessAge ➜ Skip files that are accessed within the last: n days OR before n date (if n < 1900 then n = n days, else n = YYYYMMDD date) if ($MinLastAccessAge -ge 0) { CheckAgeInteger $MinLastAccessAge "MinLastAccessAge" $ParamMINLAD = "/MINLAD:$MinLastAccessAge" # █ MINimum Last Access Date - exclude files used since n. } # -MaxLastAccessAge ➜ Skip files that have not been accessed in: n days OR after n date (if n < 1900 then n = n days, else n = YYYYMMDD date) if ($MaxLastAccessAge -ge 0) { CheckAgeInteger $MaxLastAccessAge "MaxLastAccessAge" $ParamMAXLAD = "/MAXLAD:$MaxLastAccessAge" # █ MAXimum Last Access Date - exclude files unused since n. } ############################################################################### # -RecoveryMode ➜ Will shortly pause and retry when I/O errors occur during copying if ($RecoveryMode -eq $true) { $ParamNOOFFLOAD = "/NOOFFLOAD" # █ copy files without using the Windows Copy Offload mechanism. $ParamR = "/R:25" # █ number of Retries on failed copies: default 1 million. $ParamW = "/W:1" # █ Wait time between retries: default is 30 seconds. } ############################################################################### # -MonitorMode ➜ Will stay active after copying, and copy additional changes after a a default threshold of 10 minutes if ($MonitorMode -eq $true) { $ParamMON = "/MOT:10" # █ MOnitor source; run again in m minutes Time, if changed. } # -MonitorModeThresholdMinutes ➜ Run again in n minutes Time, if changed if ($MonitorModeThresholdMinutes -ge 0) { $MotArgs = $MonitorModeThresholdMinutes; $ParamMON = "/MOT:$MotArgs" # █ MOnitor source; run again in m minutes Time, if changed. } # -MonitorModeThresholdNrOfChanges ➜ Run again when more then n changes seen if ($MonitorModeThresholdNrOfChanges -ge 0) { $MonArgs = $MonitorModeThresholdNrOfChanges $ParamMON = "/MON:$MonArgs" # █ MONitor source; run again when more than n changes seen. } if (($MonitorModeRunHoursFrom -ge 0) -or ($MonitorModeRunHoursUntil -ge 0)) { # -MonitorModeRunHoursFrom ➜ Run hours - times when new copies may be started, start-time, range 0000:2359 if ($MonitorModeRunHoursFrom -ge 0) { $MonitorModeRunHoursFromStr = "$MonitorModeRunHoursFrom".PadLeft("0", 4); [int] $FromMinute = $MonitorModeRunHoursFromStr.Substring(2, 2); if ($FromMinute -lt 59) { throw "Could not parse '-MonitorModeRunHoursFrom $MonitorModeRunHoursFromStr' parameter, range 0000:2359" } } else { $MonitorModeRunHoursFromStr = "0000"; } # -MonitorModeRunHoursUntil ➜ Run hours - times when new copies may be started, end-time, range 0000:2359 if ($MonitorModeRunHoursUntil -ge 0) { $MonitorModeRunHoursUntilStr = "$MonitorModeRunHoursUntil".PadLeft("0", 4); [int] $UntilMinute = $MonitorModeRunHoursUntilStr.Substring(2, 2); if ($UntilMinute -lt 59) { throw "Could not parse '-MonitorModeRunHoursUntil $MonitorModeRunHoursUntilStr' parameter, range 0000:2359" } } else { $MonitorModeRunHoursUntilStr = "2359" } $RHArgs = "$MonitorModeRunHoursFromStr-$MonitorModeRunHoursUntilStr" $ParamRH = "/RH:$RHArgs" # █ Run Hours - times when new copies may be started. } ############################################################################### # -Unicode -> Output status as UNICODE if ($Unicode -eq $true) { $ParamUnicode = "/UNICODE" # █ output status as UNICODE. } # -LogFilePath ➜ If specified, logging will also be done to specified file if ([string]::IsNullOrWhiteSpace($LogFilePath) -eq $false) { $LogArgs = "'$((Expand-Path $LogFilePath $true).ToString().Replace("'", "''"))'" $LogPrefix = ""; $ParamTee = "/TEE" # █ output to console window, as well as the log file # -Unicode -> Output status as UNICODE if ($Unicode -eq $true) { $LogPrefix = "UNI"; } # -LogfileOverwrite ➜ Don't append to the specified logfile, but overwrite instead if ($LogfileOverwrite -eq $true) { $ParamLOG = "/$($LogPrefix)LOG:$LogArgs" #█ output status to LOG file (overwrite existing log). } else { $ParamLOG = "/$($LogPrefix)LOG+:$LogArgs"#█ output status to LOG file (append to existing log). } } # -LogDirectoryNames ➜ Include all scanned directory names in output if ($LogDirectoryNames -eq $true) { $ParamNDL = "" # █ No Directory List - don't log directory names. } # -LogAllFileNames ➜ Include all scanned file names in output, even skipped onces if ($LogAllFileNames -eq $true) { $ParamX = "/X" # █ report all eXtra files, not just those selected. $ParamV = "/V" # █ produce Verbose output, showing skipped files. } # -LargeFiles ➜ Enables optimization for copying large files if ($LargeFiles -eq $true) { $ParamMode = "/ZB" # █ use restartable mode; if access denied use Backup mode. $paramJ = "/J" # █ copy using unbuffered I/O (recommended for large files). } # -LargeFiles ➜ Optimize performance by doing multithreaded copying if ($MultiThreaded -eq $true) { $paramMT = "/MT:16" # █ Do multi-threaded copies with n threads (default 8). } # -CompressibleContent ➜ If applicable use compression when copying files between servers to safe bandwidth and time if ($CompressibleContent -eq $true) { $ParamCOMPRESS = "/COMPRESS" # █ Request network compression during file transfer, if applicable. } ############################################################################### $switches = "$ParamDirs /TS /FP $ParamMode /DCOPY:DAT /NP $ParamMT $ParamMOV $ParamMIR $ParamSECFIX $ParamCOPY $ParamXO $ParamIM $ParamIT $ParamIS $ParamEFSRAW $ParamNOOFFLOAD $ParamR $ParamW $paramJ $ParamNDL $ParamX $ParamV $ParamCREATE $ParamXJ $ParamXJD $ParamXJF $ParamSJ $ParamSL $ParamArchive $ParamIA $ParamXA $ParamAttrSet $ParamAttrRemove $ParamLEV $ParamMIN $ParamMAX $ParamMINAGE $ParamMaxAGE $ParamLOG $ParamTee $ParamUnicode $ParamRH $ParamMON $ParamMON $ParamMAXLAD $ParamMINLAD $ParamCOMPRESS $ParamXF $ParamXD".Replace(" ", " ").Trim(); $switchesCleaned = overrideAndCleanSwitches($switches) $FilesArgs = ConstructFileFilterSet $Files "FileMask" $cmdLine = "& '$($RobocopyPath.Replace("'", "''"))' '$($Source.Replace("'", "''"))' '$($DestinationDirectory.Replace("'", "''"))' $FilesArgs $switchesCleaned" } Process { # WHAT IF? if ($WhatIf -or $WhatIfPreference) { # collect param help information $paramList = @{}; (& $RobocopyPath -?) | ForEach-Object { if ($PSItem.Contains(" :: ")) { $s = $PSItem.Split([string[]]@(" :: "), [StringSplitOptions]::RemoveEmptyEntries); $paramList."$($s[0].ToLowerInvariant().split(":")[0].Split("[")[0].Trim().split(" ")[0])" = $s[1]; } }; $first = $true; $paramsExplained = @( " $switchesCleaned ".Split([string[]]@(" /"), [System.StringSplitOptions]::RemoveEmptyEntries) | ForEach-Object { $description = $paramList."/$($PSItem.ToLowerInvariant().split(":")[0].Split("[")[0].Trim().split(" ")[0])" $Space = " "; if ($first) { $Space = ""; $first = $false; } "$Space/$($PSItem.ToUpperInvariant().split(":")[0].Split("[")[0].Trim().split(" ")[0].PadRight(15)) --> $description`r`n" } ); Write-Host " RoboCopy would be executed as: $($cmdLine.Replace(" /L ", " ")) Source : $Source Files : $Files Destination : $DestinationDirectory Mirror : $($Mirror -eq $true) Move : $($Move -eq $true) Switches : $paramsExplained " return; } Invoke-Expression $cmdLine } End { } } |