Comparing directories in Powershell
Having blogged about Compare-Object and having written MOBZync years ago and considering my newfound love of PowerShell, I thought it was about time to write Compare-Directories.ps1. Without further ado:
<#
This script compares the contents of two directories (recursively if -Recurse specified)
Output is sent to Out-GridView unless -Passthru is specified
#>
[Cmdletbinding()]
Param(
# The first path
[Parameter(Mandatory=$true)]
[string]$Path1,
# The second path
[Parameter(Mandatory=$true)]
[string]$Path2,
# Include these files (default: all)
[Parameter(Mandatory=$false)]
[string]$Filter,
# Recurse into directories
[Parameter(Mandatory=$false)]
[switch]$Recurse = $false,
# Just pass the objects, otherwise Out-GridView them
[Parameter(Mandatory=$false)]
[switch]$Passthru,
# Include files present in both folders
[Parameter(Mandatory=$false)]
[switch]$IncludeEqual,
# Exclude files NOT present in both folders
[Parameter(Mandatory=$false)]
[switch]$ExcludeDifferent
)
# Test if paths exist
if (!(Test-Path -PathType Container $Path1)) { throw "Path '$Path1' does not exist" }
if (!(Test-Path -PathType Container $Path2)) { throw "Path '$Path2' does not exist" }
# Make full path names
[string]$fullPath1 = (Resolve-Path -Path $Path1).Path
[string]$fullPath2 = (Resolve-Path -Path $Path2).Path
Write-Verbose "Comparing $fullPath1 and $fullPath2"
# Get all files in the paths
$files1 = (Get-ChildItem -Path $fullPath1 -File -Filter $Filter -Recurse:$Recurse)
$files2 = (Get-ChildItem -Path $fullPath2 -File -Filter $Filter -Recurse:$Recurse)
# If we did not find any files, use an empty array instead of $null
# Otherwise, remove the path part so the names become relative
if ($null -eq $files1) { $files1 = @() } else { $files1 = $files1.FullName.Substring($fullPath1.Length + 1) }
if ($null -eq $files2) { $files2 = @() } else { $files2 = $files2.FullName.Substring($fullPath2.Length + 1) }
# If we don't want differences, we DO want equals
if ($ExcludeDifferent) { $IncludeEqual = $true }
# Compare, then convert to @{ Name, OnlyIn }
$diffs = foreach ($diff in (Compare-Object @($files1) @($files2) -IncludeEqual:$IncludeEqual -ExcludeDifferent:$ExcludeDifferent)) {
[pscustomobject]@{
Name = $diff.InputObject
OnlyIn = switch ($diff.SideIndicator) { '<=' { $fullPath1 } '=>' { $fullPath2 } '==' { '-' } }
}
}
# Send to Out-GridView unless -Passthru
if ($Passthru) { $diffs} else { $diffs | Out-GridView -Title "Compare '$Path1' and '$Path2'" }
Not as succinct as I like, but still quite short and quite powerful.
In a nutshell:
Compare-Directories 'somepath''otherpath' -Recurse
will display a grid containing the names of files present in one directory but not in the other (and vice versa).
You can do that non-recursively and/or only for some types of files:
Compare-Directories 'somepath' 'otherpath' *.pdf
You get the drift. If you don't want the nice grid view, specify -Passthru and you'll get a nice list of objects containing the name and an OnlyIn property. You can even display just the files that are present in both directories with -ExludeDifferent or show all files using -IncludeEqual.