I once faced a similar problem. This is the method I ended up with:
function Assert-FolderMatches {
<#
.SYNOPSIS
Assert folder matches reference
.DESCRIPTION
Recursively compare folders ensuring that all files and folders have been copied correctly.
If the file length doesn't match it will also raise an error.
.PARAMETER Path
Path to compare.
.PARAMETER ReferencePath
Path to compare with (the source folder for how it should look like).
.PARAMETER AllowNewFiles
Allow files to be present in Path which are not present in ReferencePath (like log files).
#>
Param(
[Parameter(Mandatory = $true)]
[string] $Path,
[Parameter(Mandatory = $true)]
[string] $ReferencePath,
[Parameter()]
[switch] $AllowNewFiles
)
$Path = Resolve-Path $Path
$ReferencePath = Resolve-Path $ReferencePath
# Identify all files and folders.
function Get-ItemsForFolder($FolderPath) {
Get-ChildItem -Path $FolderPath -Recurse | ForEach-Object {
$relativePath = $_.FullName.Replace($FolderPath, "")
if ($relativePath.StartsWith('\') -or $relativePath.StartsWith('/')) {
$relativePath = $relativePath.Substring(1)
}
if ($_ -is [System.IO.DirectoryInfo]) {
[pscustomobject] @{
'RelativePath' = $relativePath;
'Length' = $null;
}
} else {
[pscustomobject] @{
'RelativePath' = $relativePath;
'Length' = $_.Length;
}
}
}
}
$files = Get-ItemsForFolder $Path
$matchFiles = Get-ItemsForFolder $ReferencePath
# Compare items from both sides.
$diff = Compare-Object -ReferenceObject $matchFiles `
-DifferenceObject $files `
-Property RelativePath,Length
# Filter out additions from 'Path' side if requested.
if ($AllowNewFiles) {
$diff = $diff | Where-Object { $_.SideIndicator -ne '=>' }
}
return $diff
}
Not the most simple solution, maybe someone has a more elegant way to do this. Essentially I'm using relative paths to compare, instead of just the file name.
And here's an example output. So you can see that "b.txt" exists in both target and source folder, but in a different location and it's raised as a difference:
$> Assert-FolderMatches -ReferencePath C:\Temp\a -Path C:\Temp\b
RelativePath Length SideIndicator
------------ ------ -------------
f2 =>
f2\b.txt 0 =>
f1 <=
f1\b.txt 0 <=
Just as a note, I'm not comparing actual file content. I tried to do this with hashing, but it was too slow for my use case. So I ended up just comparing file size, which seems a reasonable compromise for copying to me, unless you're really worried about file corruption in some way.