PowerShell File Comparison: How to Diff Text Files Like Unix Diff Command


3 views

When transitioning from Unix/Linux to Windows PowerShell, one of the first hurdles developers encounter is file comparison. While Unix systems have the powerful diff command, PowerShell requires a different approach.

The native Compare-Object cmdlet often produces confusing output when used directly with files:

PS C:\> compare-object file1.txt file2.txt

InputObject       SideIndicator
-----------       -------------
file2.txt         =>
file1.txt         <=

This happens because the cmdlet is comparing file objects rather than their contents.

To compare actual file contents, you need to read the files first:

$file1 = Get-Content -Path .\file1.txt
$file2 = Get-Content -Path .\file2.txt
Compare-Object $file1 $file2

This will properly show line-by-line differences between the files.

For more readable output similar to Unix diff:

Compare-Object $file1 $file2 | 
    ForEach-Object {
        if ($_.SideIndicator -eq "=>") {
            Write-Host ("+ " + $_.InputObject) -ForegroundColor Green
        } else {
            Write-Host ("- " + $_.InputObject) -ForegroundColor Red
        }
    }

For more advanced scenarios, consider these options:

  • Windows Subsystem for Linux (WSL): Run native Unix diff command
  • git diff: If files are in a Git repository
  • Third-party modules: PSStringScanner or PSReadLine

For large files, use streaming comparison to avoid memory issues:

$reader1 = [System.IO.File]::OpenText("file1.txt")
$reader2 = [System.IO.File]::OpenText("file2.txt")

while (($line1 = $reader1.ReadLine()) -ne $null) {
    $line2 = $reader2.ReadLine()
    if ($line1 -ne $line2) {
        Write-Host "Difference at line $($reader1.BaseStream.Position):"
        Write-Host "< $line1" -ForegroundColor Red
        Write-Host "> $line2" -ForegroundColor Green
    }
}

For regular use, create a reusable diff function in your profile:

function Compare-Files {
    param(
        [string]$Path1,
        [string]$Path2
    )
    
    $content1 = Get-Content $Path1
    $content2 = Get-Content $Path2
    
    Compare-Object $content1 $content2 | 
        Where-Object { $_.SideIndicator -eq "=>" } |
        Select-Object @{
            Name="Line";Expression={$_.InputObject}
        },@{Name="File";Expression={$Path2}}
}

When transitioning from Unix/Linux to Windows, many developers miss the handy diff command. PowerShell's Compare-Object is the native alternative, but its output format often confuses newcomers. Let's explore how to properly compare text files in PowerShell with practical examples.

Here's the fundamental command structure:

Compare-Object -ReferenceObject (Get-Content file1.txt) -DifferenceObject (Get-Content file2.txt)

This compares the contents of file1.txt (reference) against file2.txt (difference). The output shows lines unique to each file.

To get Unix-like side-by-side comparison, use this enhanced version:

Compare-Object (Get-Content file1.txt) (Get-Content file2.txt) -IncludeEqual |
  Format-Table -Property @{Label="File1";Expression={if ($_.SideIndicator -eq "<=" -or $_.SideIndicator -eq "==") {$_.InputObject} else {""}}},
                        @{Label="File2";Expression={if ($_.SideIndicator -eq "=>" -or $_.SideIndicator -eq "==") {$_.InputObject} else {""}}},
                        @{Label="Status";Expression={switch ($_.SideIndicator) {"<=" {"Unique to File1"} "=>" {"Unique to File2"} "==" {"Match"}}}}

For large files, use streaming to avoid memory issues:

$file1 = [System.IO.File]::ReadLines("C:\path\to\file1.txt")
$file2 = [System.IO.File]::ReadLines("C:\path\to\file2.txt")
Compare-Object $file1 $file2

For regular use, create a custom function in your profile:

function Get-FileDiff {
    param(
        [Parameter(Mandatory=$true)]$File1,
        [Parameter(Mandatory=$true)]$File2
    )
    Compare-Object (Get-Content $File1) (Get-Content $File2) -IncludeEqual |
        ForEach-Object {
            $result = [PSCustomObject]@{
                LineNumber = $_.InputObject.ReadCount
                Content = $_.InputObject
                Status = switch ($_.SideIndicator) {
                    "<=" {"LeftOnly"}
                    "=>" {"RightOnly"}
                    "==" {"Equal"}
                }
            }
            $result
        }
}

For simple cases, the built-in fc (file compare) command works:

fc file1.txt file2.txt

This shows differences with line numbers and surrounding context.

For complex comparisons, consider integrating external diff tools:

# Using WinMerge (must be installed)
Start-Process "WinMergeU.exe" -ArgumentList "file1.txt file2.txt"