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.