Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@victorvogelpoel
Last active November 30, 2021 09:20
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save victorvogelpoel/6636754 to your computer and use it in GitHub Desktop.
Save victorvogelpoel/6636754 to your computer and use it in GitHub Desktop.
# Compare-Directory.ps1
# Compare files in one or more directories and return file difference results
# Victor Vogelpoel <victor@victorvogelpoel.nl>
# Sept 2013
#
# Disclaimer
# This script is provided AS IS without warranty of any kind. I disclaim all implied warranties including, without limitation,
# any implied warranties of merchantability or of fitness for a particular purpose. The entire risk arising out of the use or
# performance of the sample scripts and documentation remains with you. In no event shall I be liable for any damages whatsoever
# (including, without limitation, damages for loss of business profits, business interruption, loss of business information,
# or other pecuniary loss) arising out of the use of or inability to use the script or documentation.
# Compare-Directory -ReferenceDirectory "C:\Compare-Directory\FrontEnd1-Site" -DifferenceDirectory "C:\Compare-Directory\FrontEnd2-Site"
function Compare-Directory
{
[CmdletBinding()]
param
(
[Parameter(Mandatory=$true, position=0, ValueFromPipelineByPropertyName=$true, HelpMessage="The reference directory to compare one or more difference directories to.")]
[System.IO.DirectoryInfo]$ReferenceDirectory,
[Parameter(Mandatory=$true, position=1, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, HelpMessage="One or more directories to compare to the reference directory.")]
[System.IO.DirectoryInfo[]]$DifferenceDirectory,
[Parameter(Mandatory=$false, ValueFromPipelineByPropertyName=$true, HelpMessage="Recurse the directories")]
[switch]$Recurse,
[Parameter(Mandatory=$false, ValueFromPipelineByPropertyName=$true, HelpMessage="Files to exclude from the comparison")]
[String[]]$ExcludeFile,
[Parameter(Mandatory=$false, ValueFromPipelineByPropertyName=$true, HelpMessage="Directories to exclude from the comparison")]
[String[]]$ExcludeDirectory,
[Parameter(Mandatory=$false, ValueFromPipelineByPropertyName=$true, HelpMessage="Displays only the characteristics of compared objects that are equal.")]
[switch]$ExcludeDifferent,
[Parameter(Mandatory=$false, ValueFromPipelineByPropertyName=$true, HelpMessage="Displays characteristics of files that are equal. By default, only characteristics that differ between the reference and difference files are displayed.")]
[switch]$IncludeEqual,
[Parameter(Mandatory=$false, ValueFromPipelineByPropertyName=$true, HelpMessage="Passes the objects that differed to the pipeline.")]
[switch]$PassThru
)
begin
{
function Get-MD5
{
[CmdletBinding(SupportsShouldProcess=$false)]
param
(
[Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, HelpMessage="file(s) to create hash for")]
[Alias("File", "Path", "PSPath", "String")]
[ValidateNotNull()]
$InputObject
)
begin
{
$cryptoServiceProvider = [System.Security.Cryptography.MD5CryptoServiceProvider]
$hashAlgorithm = new-object $cryptoServiceProvider
}
process
{
$hashByteArray = ""
$item = Get-Item $InputObject -ErrorAction SilentlyContinue
if ($item -is [System.IO.DirectoryInfo]) { throw "Cannot create hash for directory" }
if ($item) { $InputObject = $item }
if ($InputObject -is [System.IO.FileInfo])
{
$stream = $null;
$hashByteArray = $null
try
{
$stream = $InputObject.OpenRead();
$hashByteArray = $hashAlgorithm.ComputeHash($stream);
}
finally
{
if ($stream -ne $null)
{
$stream.Close();
}
}
}
else
{
$utf8 = new-object -TypeName "System.Text.UTF8Encoding"
$hashByteArray = $hashAlgorithm.ComputeHash($utf8.GetBytes($InputObject.ToString()));
}
Write-Output ([BitConverter]::ToString($hashByteArray)).Replace("-","")
}
}
function Get-Files
{
[CmdletBinding(SupportsShouldProcess=$false)]
param
(
[string]$DirectoryPath,
[String[]]$ExcludeFile,
[String[]]$ExcludeDirectory,
[switch]$Recurse
)
$relativeBasenameIndex = $DirectoryPath.ToString().Length
# Get the files from the first deploypath
# and ADD the MD5 hash for the file as a property
# and ADD a filepath relative to the deploypath as a property
Get-ChildItem -Path $DirectoryPath -Exclude $ExcludeFile -Recurse:$Recurse | foreach {
$hash = ""
if (!$_.PSIsContainer) { $hash = Get-MD5 $_ }
# Added two new properties to the DirectoryInfo/FileInfo objects
$item = $_ |
Add-Member -Name "MD5Hash" -MemberType NoteProperty -Value $hash -PassThru |
Add-Member -Name "RelativeBaseName" -MemberType NoteProperty -Value ($_.FullName.Substring($relativeBasenameIndex)) -PassThru
# Test for directories and files that need to be excluded because of ExcludeDirectory
if ($item.PSIsContainer) { $item.RelativeBaseName += "\" }
if ($ExcludeDirectory | where { $item.RelativeBaseName -like "\$_\*" })
{
Write-Verbose "Ignore item `"$($item.Fullname)`""
}
else
{
Write-Verbose "Adding `"$($item.Fullname)`" to result set"
Write-Output $item
}
}
}
$referenceDirectoryFiles = Get-Files -DirectoryPath $referenceDirectory -ExcludeFile $ExcludeFile -ExcludeDirectory $ExcludeDirectory -Recurse:$Recurse
}
process
{
if ($DifferenceDirectory -and $referenceDirectoryFiles)
{
foreach($nextPath in $DifferenceDirectory)
{
$nextDifferenceFiles = Get-Files -DirectoryPath $nextpath -ExcludeFile $ExcludeFile -ExcludeDirectory $ExcludeDirectory -Recurse:$Recurse
###################################################
# Compare the contents of the two file/directory arrays and return the results
$results = @(Compare-Object -ReferenceObject $referenceDirectoryFiles -DifferenceObject $nextDifferenceFiles -ExcludeDifferent:$ExcludeDifferent -IncludeEqual:$IncludeEqual -PassThru:$PassThru -Property RelativeBaseName, MD5Hash)
if (!$PassThru)
{
foreach ($result in $results)
{
$path = $ReferenceDirectory
$pathFiles = $referenceDirectoryFiles
if ($result.SideIndicator -eq "=>")
{
$path = $nextPath
$pathFiles = $nextDifferenceFiles
}
# Find the original item in the files array
$itemPath = (Join-Path $path $result.RelativeBaseName).ToString().TrimEnd('\')
$item = $pathFiles | where { $_.fullName -eq $itemPath }
$result | Add-Member -Name "Item" -MemberType NoteProperty -Value $item
}
}
Write-Output $results
}
}
}
<#
.SYNOPSIS
Compares a reference directory with one or more difference directories.
.DESCRIPTION
Compare-Directory compares a reference directory with one ore more difference
directories. Files and directories are compared both on filename and contents
using a MD5hash.
Internally, Compare-Object is used to compare the directories. The behavior
and results of Compare-Directory is similar to Compare-Object.
.PARAMETER ReferenceDirectory
The reference directory to compare one or more difference directories to.
.PARAMETER DifferenceDirectory
One or more directories to compare to the reference directory.
.PARAMETER Recurse
Include subdirectories in the comparison.
.PARAMETER ExcludeFile
File names to exclude from the comparison.
.PARAMETER ExcludeDirectory
Directory names to exclude from the comparison. Directory names are
relative to the Reference of Difference Directory path
.PARAMETER ExcludeDifferent
Displays only the characteristics of compared files that are equal.
.PARAMETER IncludeEqual
Displays characteristics of files that are equal. By default, only
characteristics that differ between the reference and difference files
are displayed.
.PARAMETER PassThru
Passes the objects that differed to the pipeline. By default, this
cmdlet does not generate any output.
.EXAMPLE
Compare-Directory -reference "D:\TEMP\CompareTest\path1" -difference "D:\TEMP\CompareTest\path2" -ExcludeFile "web.config" -recurse
Compares directories "D:\TEMP\CompareTest\path1" and "D:\TEMP\CompareTest\path2" recursively, excluding "web.config"
Only differences are shown. Results:
RelativeBaseName MD5Hash SideIndicator Item
---------------- ------- ------------- ----
bin\site.dll 87A1E6006C2655252042F16CBD7FB41B => D:\TEMP\CompareTest\path2\bin\site.dll
index.html 02BB8A33E1094E547CA41B9E171A267B => D:\TEMP\CompareTest\path2\index.html
index.html 20EE266D1B23BCA649FEC8385E5DA09D <= D:\TEMP\CompareTest\path1\index.html
web_2.config 5E6B13B107ED7A921AEBF17F4F8FE7AF <= D:\TEMP\CompareTest\path1\web_2.config
bin\site.dll 87A1E6006C2655252042F16CBD7FB41B => D:\TEMP\CompareTest\path2\bin\site.dll
index.html 02BB8A33E1094E547CA41B9E171A267B => D:\TEMP\CompareTest\path2\index.html
index.html 20EE266D1B23BCA649FEC8385E5DA09D <= D:\TEMP\CompareTest\path1\index.html
web_2.config 5E6B13B107ED7A921AEBF17F4F8FE7AF <= D:\TEMP\CompareTest\path1\web_2.config
.EXAMPLE
Compare-Directory -reference "D:\TEMP\CompareTest\path1" -difference "D:\TEMP\CompareTest\path2" -ExcludeFile "web.config" -recurse -IncludeEqual
Compares directories "D:\TEMP\CompareTest\path1" and "D:\TEMP\CompareTest\path2" recursively, excluding "web.config".
Results include the items that are equal:
RelativeBaseName MD5Hash SideIndicator Item
---------------- ------- ------------- ----
bin == D:\TEMP\CompareTest\path1\bin
bin\site2.dll 98B68D681A8D40FA943D90588E94D1A9 == D:\TEMP\CompareTest\path1\bin\site2.dll
bin\site3.dll 9408C4B29F82260CBBA528342CBAA80F == D:\TEMP\CompareTest\path1\bin\site3.dll
bin\site4.dll 0616E1FBE12D468F611F07768D70C2EE == D:\TEMP\CompareTest\path1\bin\site4.dll
...
bin\site8.dll 87A1E6006C2655252042F16CBD7FB41B => D:\TEMP\CompareTest\path2\bin\site8.dll
index.html 02BB8A33E1094E547CA41B9E171A267B => D:\TEMP\CompareTest\path2\index.html
index.html 20EE266D1B23BCA649FEC8385E5DA09D <= D:\TEMP\CompareTest\path1\index.html
web_2.config 5E6B13B107ED7A921AEBF17F4F8FE7AF <= D:\TEMP\CompareTest\path1\web_2.config
.EXAMPLE
Compare-Directory -reference "D:\TEMP\CompareTest\path1" -difference "D:\TEMP\CompareTest\path2" -ExcludeFile "web.config" -recurse -ExcludeDifference
Compares directories "D:\TEMP\CompareTest\path1" and "D:\TEMP\CompareTest\path2" recursively, excluding "web.config".
Results only include the files that are equal; different files are excluded from the results.
.EXAMPLE
Compare-Directory -reference "D:\TEMP\CompareTest\path1" -difference "D:\TEMP\CompareTest\path2" -ExcludeFile "web.config" -recurse -Passthru
Compares directories "D:\TEMP\CompareTest\path1" and "D:\TEMP\CompareTest\path2" recursively, excluding "web.config" and returns NO comparison
results, but the different files themselves!
FullName
--------
D:\TEMP\CompareTest\path2\bin\site3.dll
D:\TEMP\CompareTest\path2\index.html
D:\TEMP\CompareTest\path1\index.html
D:\TEMP\CompareTest\path1\web_2.config
.LINK
Compare-Object
#>
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment