Recursive folder comparison with PowerShell
The Issue
This post definitely isn’t “new” or revolutionary, but I was quite surprised to find the Compare-object helper in PS, and I’m bound to forget in the future…
As part of some recent roadmap work, we moved over to a new installer technology for some of our tooling. This came with some minor headaches such as validating we have harvested all the correct files. The first iteration of this was a manual check which obviously is prone is human error – aside from being mind numbing!
I didn’t really want to use a third party tool. WinMerge can perform fantastic comparisons, but I wanted something quick and custom. Ideally also not spending longer than 10 minutes creating any code!
The first iteration was to do a recursive loop, pull out all the file names (Note: not the path) into 2 separate text files. The only “nicety” was I wrapped directory names in square brackets to give it some organisation.
The downside of this is that it only really worked for my sample folder with a few items. In production with thousands of files and nested folders this was plain chaos. Also I had to compare these files in a third party tool like WinMerge anyway – taking away the point of doing this!
The final version of my script aimed to only show the difference (avoid noise), ideally show which direction the change occurred using Compare-Object in PowerShell.
The Result
- Do a recursive loop through the directory structure
- Output Folder names as [Folder], and recursively dive-down. This is a bit dirty as I didn’t want the full path (harder to compare) but wanted to differentiate when I dug down. YMMV.
- Output File names, excluding some files I didn’t care about (Like .tmp & .XML files)
- Do this for folder A and folder B, storing the result to a variable
- Using Compare-Object on these variables and outputting the result.
function GetFiles($path, [string[]]$excludedFiles)
{
foreach ($item in Get-ChildItem $path)
{
if ($excludedFiles | Where {$item -like $_}) { continue }
if( (Get-Item $item.FullName) -is [System.IO.DirectoryInfo]){
$('['+$item.Name+']')
}else{
$($item.Name)
}
if (Test-Path $item.FullName -PathType Container)
{
GetFiles $item.FullName $excludedFiles
}
}
}
$env1 = GetFiles -path "C:\folderA\" -excludedFiles "*.xml",".tmp"
$env2 = GetFiles -path "C:\folderB\" -excludedFiles "*.xml",".tmp"
Compare-Object -DifferenceObject $env1 -ReferenceObject $env2
Which provides output like:
This could definitely be optimized and cleaned up for sure, and YMMV massively.
Overall, a few minutes in PowerShell and I managed to save substantial time – and that was my only real goal!