Functions/Invoke-ParallelRobocopy.ps1


function Invoke-ParallelRobocopy {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)] [string] $Source,
        [Parameter(Mandatory)] [string] $Destination,
        [Parameter()] [string] $ExcludeDirectories,
        [Parameter()] [int] $MaxParallelJobs = 2
    )

    if ($Source[-1] -eq "\") {
        $Source = $Source.Substring(0, ($Source.Length - 1))
    }
    if ($Destination[-1] -eq "\") {
        $Destination = $Destination.Substring(0, ($Destination.Length - 1))
    }
    Write-Verbose $Source
    Write-Verbose $Destination

    $dirs = Get-ChildItem $Source -Directory
    if ($ExcludeDirectories) {
        $dirs = $dirs | Where-Object Name -NotMatch $ExcludeDirectories
    }
    Write-Verbose ($dirs | Format-Table | Out-String)

    $processRc = @{}

    $dirs | ForEach-Object {

        $i++
        $subSource = $_.FullName
        $subDestination = Join-Path $Destination $_.Name
        $processRc[$i] = Start-Process -FilePath "robocopy.exe" -ArgumentList "`"$($subSource)`" `"$($subDestination)`" /MIR /NFL /NDL /W:1 /R:1" -PassThru -WindowStyle Minimized

        while (($processRc | ForEach-Object { $_.Values | Where-Object HasExited -EQ $false }).count -ge $MaxParallelJobs) {
            Write-Warning "Waiting to finish $(($processRc | ForEach-Object { $_.Values | Where-Object HasExited -EQ $false }).count) robocopy jobs"
            Start-Sleep 1
        }

    }

    while ($processRc | ForEach-Object { $_.Values | Where-Object HasExited -EQ $false }) {
        Write-Warning "Waiting to finish $(($processRc | ForEach-Object { $_.Values | Where-Object HasExited -EQ $false }).count) robocopy jobs before final sync"
        Start-Sleep 1
    }

    Start-Process -FilePath "robocopy.exe" -ArgumentList "`"$($Source)`" `"$($Destination)`" /MIR /NFL /NDL /W:1 /R:1" -NoNewWindow -Wait

}