Created
December 2, 2016 21:59
-
-
Save pinecones-sx/10946c478b8aeb9b090bf35e4e606a80 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
Verify contents between two tree structures and copy newer or missing files from the source to the destination | |
#> | |
function Global:Copy-Diff{ | |
param($SourcePath,$DestinationPath,[switch]$Informational,[switch]$Force) | |
# Variable setup... | |
# Verify paths & normalize path names | |
$sourceExists = Test-Path $SourcePath | |
$destinationExists = Test-Path $DestinationPath | |
If((-not $sourceExists) -or (-not $destinationExists)){Return} | |
Else{ | |
$SourcePath = Get-Item $SourcePath | Select -ExpandProperty FullName | |
$DestinationPath = Get-Item $DestinationPath | Select -ExpandProperty FullName | |
} | |
# Get tree of source path. Add properties for source path and relative path to each item. | |
$sourceTree = Get-ChildItem $SourcePath -Recurse -Force | | |
Add-Member -MemberType ScriptProperty -Name 'RootLocation' -Force -PassThru ` | |
-Value {$SourcePath} | | |
Add-Member -MemberType ScriptProperty -Name 'RelativePath' -Force -PassThru ` | |
-Value {$this.FullName -replace ('.*' + [regex]::escape($SourcePath + '\'))} | | |
Add-Member -MemberType ScriptProperty -Name 'Info' -Force -PassThru ` | |
-Value {'Source object.'} | |
# Get tree of destination path. Add properties for source path and relative path to each item. | |
$destinationTree = Get-ChildItem $DestinationPath -Recurse -Force | | |
Add-Member -MemberType ScriptProperty -Name 'RootLocation' -Force -PassThru ` | |
-Value {$DestinationPath} | | |
Add-Member -MemberType ScriptProperty -Name 'RelativePath' -Force -PassThru ` | |
-Value {$this.FullName -replace ('.*' + [regex]::escape($DestinationPath + '\'))} | | |
Add-Member -MemberType ScriptProperty -Name 'Info' -Force -PassThru ` | |
-Value {'Destination object.'} | |
# declare work arrays | |
$workInfoDestIsDifferent = @() | |
$workCompareEqualFiles = @() | |
$workCopyToDestination = @() | |
$DiffTree = Compare $SourceTree $DestinationTree -IncludeEqual | |
# Sort each diff object out into appropriate work arrays. | |
ForEach ($diff in $DiffTree){ | |
Switch ($diff.SideIndicator){ | |
'=>' { # Present in destination only. | |
$workInfoDestIsDifferent += $diff.InputObject | | |
Add-Member -MemberType ScriptProperty -Name 'Info' -Force -PassThru ` | |
-Value {'Present only at destination.'} | |
} | |
'<=' { # Present in source only. | |
$workCopyToDestination += $diff.InputObject | | |
Add-Member -MemberType ScriptProperty -Name 'Info' -Force -PassThru ` | |
-Value {'Present only at source.'} | |
} | |
'==' { # Present in both source & destination. | |
$workCompareEqualFiles += $diff.InputObject | | |
Add-Member -MemberType ScriptProperty -Name 'Info' -Force -PassThru ` | |
-Value {'Present at both locations.'} | |
} | |
default {Write-Error -Message ('Found crazy SideIndicator. File ' + $diff.InputObject.FullName)} | |
} # End Switch | |
} # End sorting foreach | |
# Compare hashes of '==' objects | |
ForEach ($sameObject in $workCompareEqualFiles) { | |
# ***".InputObject" should always be the source item when side indicator is '==' | |
If ($sameObject.PSIsContainer -eq $true){ | |
$sameObject = $sameObject | | |
Add-Member -MemberType ScriptProperty -Name 'Info' -Force -PassThru ` | |
-Value {'Folder present in both locations.'} | |
} | |
Else{ | |
# Get source object. Add hash as property. | |
$objSource = Get-Item $sameObject.FullName | | |
Add-Member -MemberType ScriptProperty -Name 'FileHash' -Force -PassThru ` | |
-Value {$this | Get-FileHash | Select -ExpandProperty Hash} | |
# Get destination object. Add hash as property. | |
$uncObjDest = ($DestinationPath + '\' + $sameObject.RelativePath) | |
$objDest = Get-Item $uncObjDest | | |
Add-Member -MemberType ScriptProperty -Name 'FileHash' -Force -PassThru ` | |
-Value {$this | Get-FileHash | Select -ExpandProperty Hash} | |
# If hashes don't match... | |
If ($objSource.FileHash -ne $objDest.FileHash) { | |
# If destination object was modified most recently... | |
If ($objSource.LastWriteTime -lt $objDest.LastWriteTime){ | |
$workInfoDestIsDifferent += Get-Item $objDest.FullName | | |
Add-Member -MemberType ScriptProperty -Name 'RootLocation' -Force -PassThru ` | |
-Value {$DestinationPath} | | |
Add-Member -MemberType ScriptProperty -Name 'RelativePath' -Force -PassThru ` | |
-Value {$this.FullName -replace ('.*' + [regex]::escape($DestinationPath + '\'))} | | |
Add-Member -MemberType ScriptProperty -Name 'Info' -Force -PassThru ` | |
-Value {'Present only at destination.'} | |
} | |
Else{ | |
$workCopyToDestination += $sameObject | | |
Add-Member -MemberType ScriptProperty -Name 'Info' -Force -PassThru ` | |
-Value {'Newer copy at source.'} | |
} | |
} # End if hashed don't match | |
} # End Else if | |
} # End foreach hash compare | |
# Prompt for write if -Force isn't used | |
If ((-not $Force) -and (-not $Informational)){ | |
Write-Host 'Comparisons finished. Begin copy/overwrite?' | |
$userAnswer = Read-Host '(y/n)' | |
Switch ($userAnswer){ | |
'y'{Write-Host 'Starting copy process...'} | |
'n'{$Informational = $true} | |
deafult{ | |
$Informational = $true | |
Write-Host 'Invalid selection. Returning information only. (accpets only "y" or "n")' | |
Pause | |
} | |
} | |
} | |
# Process copies or return information only | |
If ($Informational){ | |
$returnArray = ($workCopyToDestination += $workInfoDestIsDifferent) | | |
Select Name,FullName,Info,PSIsContainer,Length,LastWriteTime,RootLocation,RelativePath | |
Return $returnArray | |
} | |
Else{ | |
$returnArray = @() | |
ForEach ($workObject in $workCopyToDestination) { | |
$from = ($workObject.FullName) | |
$to = ($workObject.FullName -replace [regex]::Escape($workObject.RootLocation),$DestinationPath) | |
# DO WORK! | |
Copy-Item $from $to -Force > $null | |
$newInfoText = ($workObject.Info + ' Object Copied.') | |
$returnArray += $workObject | | |
Add-Member -MemberType ScriptProperty -Name 'Info' -Force -PassThru ` | |
-Value {$newInfoText} | |
} | |
$returnArray = $returnArray | | |
Select Name,FullName,Info,PSIsContainer,Length,LastWriteTime | |
Return $returnArray | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment