144 lines
4.2 KiB
PowerShell
144 lines
4.2 KiB
PowerShell
|
[CmdletBinding(SupportsShouldProcess,ConfirmImpact='Medium')]param(
|
||
|
[Parameter(Mandatory,ParameterSetName="RemoteSrc")]
|
||
|
[string]$RemoteSrc,
|
||
|
[Parameter(Mandatory,ParameterSetName="Local",Position=1)]
|
||
|
[Parameter(Mandatory,ParameterSetName="RemoteSrc",Position=1)]
|
||
|
[Parameter(Mandatory,ParameterSetName="RemoteDst",Position=1)]
|
||
|
[string]$SrcBase,
|
||
|
[Parameter(Mandatory,ParameterSetName="Local",Position=2)]
|
||
|
[Parameter(Mandatory,ParameterSetName="RemoteSrc",Position=2)]
|
||
|
[Parameter(Mandatory,ParameterSetName="RemoteDst",Position=2)]
|
||
|
[string]$DstBase,
|
||
|
[Parameter(Mandatory,ParameterSetName="RemoteDst")]
|
||
|
[string]$RemoteDst,
|
||
|
[Parameter(Position=4)]
|
||
|
[string]$Path=".",
|
||
|
[switch]$NoPassThru,
|
||
|
[int]$Depth=-1,
|
||
|
[int]$Limit,
|
||
|
[Alias("Do")]
|
||
|
[switch]$NoSimulate
|
||
|
)
|
||
|
Write-Host -ForegroundColor Cyan ( $PSCmdlet | Out-String )
|
||
|
Write-Host -ForegroundColor Yellow ( $PSBoundParameters | Out-String )
|
||
|
|
||
|
function GetLocalPath{
|
||
|
[CmdletBinding(SupportsShouldProcess,ConfirmImpact='Low')]param(
|
||
|
[string]$LocalBase,
|
||
|
[string]$Path = "."
|
||
|
)
|
||
|
return ([System.IO.Path]::GetFullPath((Join-Path $LocalBase $Path),"/"))
|
||
|
}
|
||
|
|
||
|
function GetRemotePath{
|
||
|
[CmdletBinding(SupportsShouldProcess,ConfirmImpact='Low')]param(
|
||
|
[string]$RemoteAddr,
|
||
|
[string]$RemoteBase,
|
||
|
[string]$Path = "."
|
||
|
)
|
||
|
return "$RemoteAddr`:$(
|
||
|
[Management.Automation.WildcardPattern]::Escape(
|
||
|
[System.IO.Path]::GetFullPath((Join-Path $RemoteBase $Path),"/")
|
||
|
) -replace '([&\[\(\)\\])','\$1'
|
||
|
)"
|
||
|
}
|
||
|
|
||
|
function GetSubstituedPath{
|
||
|
[CmdletBinding(SupportsShouldProcess,ConfirmImpact='Low')]param(
|
||
|
[string]$OriginalPath,
|
||
|
[string]$Base,
|
||
|
[string]$ReplaceBase,
|
||
|
[string]$RemotePrefix
|
||
|
)
|
||
|
|
||
|
$local:newPath = $OriginalPath -replace "^$([regex]::Escape($Base))",($ReplaceBase -replace '\$(\d)','\$$1')
|
||
|
if( $RemotePrefix ) {
|
||
|
$newPath = GetRemotePath $RemotePrefix $newPath
|
||
|
}
|
||
|
return $newPath
|
||
|
}
|
||
|
|
||
|
|
||
|
$local:SrcPath = GetLocalPath $SrcBase $Path
|
||
|
$local:DstPath = GetLocalPath $DstBase $Path
|
||
|
$local:LocalPath = [string]::Empty
|
||
|
|
||
|
if( -not $RemoteSrc ) {
|
||
|
$LocalPath = $SrcPath
|
||
|
} else {
|
||
|
$LocalPath = $DstPath
|
||
|
}
|
||
|
|
||
|
function GetSortedDirsDepthFirst {
|
||
|
param([string]$Path,$StopDepth=-1,$CurrentDepth=0)
|
||
|
|
||
|
$local:dirs = Get-ChildItem $Path -Directory |
|
||
|
Sort-Object Name
|
||
|
foreach( $local:d in $dirs ) {
|
||
|
if( $StopDepth -lt 0 -or $CurrentDepth -lt $StopDepth ) {
|
||
|
GetSortedDirsDepthFirst $d.FullName $StopDepth ($CurrentDepth + 1)
|
||
|
}
|
||
|
$d
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$local:limitParam = @{}
|
||
|
if( $Limit -gt 0 ) {
|
||
|
$limitParam.First = $Limit
|
||
|
}
|
||
|
$local:Pairs = @()
|
||
|
if( -not $RemoteSrc ) {
|
||
|
$Pairs = GetSortedDirsDepthFirst $LocalPath -StopDepth $Depth |
|
||
|
Select-Object @limitParam |
|
||
|
ForEach-Object {
|
||
|
[PSCustomObject]([ordered]@{
|
||
|
SrcGB = -1
|
||
|
Src = GetLocalPath $_.FullName
|
||
|
Dst = GetSubstituedPath $_.FullName $SrcPath $DstPath $RemoteDst
|
||
|
})
|
||
|
}
|
||
|
} else {
|
||
|
$Pairs = , ([PSCustomObject]([ordered]@{
|
||
|
Src = GetRemotePath $RemoteSrc $SrcPath
|
||
|
Dst = $DstPath
|
||
|
}))
|
||
|
}
|
||
|
|
||
|
$local:sw = [System.Diagnostics.Stopwatch]::StartNew();
|
||
|
$Pairs | ForEach-Object {
|
||
|
$local:s = $_.Src -replace "'",'${Q}'
|
||
|
$local:d = $_.Dst -replace "'",'\\${Q}'
|
||
|
$local:skip = $false
|
||
|
if( $_.SrcGB ) {
|
||
|
$_.SrcGB = [Math]::Round( (
|
||
|
Get-ChildItem -Recurse -File $_.Src | Measure-Object -Sum Length
|
||
|
).Sum / 1000000000, 2)
|
||
|
if( -not $_.SrcGB ) {
|
||
|
$_.SrcGB = "Skip"
|
||
|
} else {
|
||
|
$_.SrcGB = "$($_.SrcGB) GB"
|
||
|
}
|
||
|
}
|
||
|
Write-Host -ForegroundColor Cyan ($($_ | Format-Table -HideTableHeaders | Out-String).Trim())
|
||
|
$local:exp = "Q=\`"'\`"; rsync -avzh --remove-source-files --progress \`"$s/\`" \`"$d/\`""
|
||
|
|
||
|
$sw.Restart()
|
||
|
if( -not $_.SrcGB -or $_.SrcGB -ne "Skip" ) {
|
||
|
$exp | Out-Host
|
||
|
|
||
|
sh -c $exp | Out-Host
|
||
|
}
|
||
|
|
||
|
Write-Host -Cyan "Cleanup '$s'..."
|
||
|
$exp = "Q=\`"'\`"; find \`"$s\`" -type d -empty -delete"
|
||
|
sh -c $exp | Out-Host
|
||
|
|
||
|
[PSCustomObject]([ordered]@{
|
||
|
PSTypeName = 'RsyncReport'
|
||
|
SrcGB = $_.SrcGB
|
||
|
Elapsed = $sw.Elapsed
|
||
|
Src = $_.Src
|
||
|
Dst = $_.Dst
|
||
|
})
|
||
|
}
|