PowerShell function that compares the files in one folder, with the files in another folder.
#Requires -Version 4.0
# https://github.com/MathieuBuisson/Powershell-Utility/blob/master/Compare-FolderFiles/Compare-FolderFiles.psm1
function Compare-FolderFiles {
<#
.SYNOPSIS
Compares the files in one folder with the files in another folder.
.DESCRIPTION
Compares the files in one folder with the files in another folder.
Uses a MD5 hash value to uniquely identify and compare the content of the files.
.PARAMETER ReferenceFolder
Folder used as a reference for comparison.
The command checks that the value for this parameter is a valid path.
.PARAMETER DifferenceFolder
Specifies the folder that is compared to the reference folder.
Accepts pipeline input, and the command checks that the value for this parameter is a valid path.
.PARAMETER Recurse
Compares all files in the Reference folder and difference folder, including their child folders.
.PARAMETER Force
Includes the hidden files and hidden child folders.
.PARAMETER ShowDifferenceSide
Shows only the different files which are located in the difference folder.
.PARAMETER ShowReferenceSide
Shows only the different files which are located in the reference folder.
.PARAMETER ShowNewer
For files with the same name which exist in the same location in the reference folder and the `
difference folder (but have different hash values), this shows only the newer version of the file
.EXAMPLE
Compare-FolderFiles -ReferenceFolder C:\test -DifferenceFolder C:\test2 -Force
Compares the files in C:\test2 with the files in C:\test, including hidden files
.EXAMPLE
"C:\test2" | Compare-FolderFiles -ReferenceFolder C:\test -Recurse -ShowReferenceSide
Compares the files in the path specified from pipeline input recursively to the files in C:\test `
and shows only the different files which are located in the reference folder
.EXAMPLE
Compare-FolderFiles C:\test C:\test2 -ShowNewer
Compares the files in C:\test2 with the files in C:\test, showing only the more recent version of `
files which have the same name and are in the same location on both sides.
.LINK
Get-FileHash
#>
[CmdletBinding()]
Param (
[Parameter(Mandatory=$True,Position=0)]
[validatescript({ Test-Path $_ })]
[string]$ReferenceFolder,
[Parameter(Mandatory=$True,ValueFromPipeline=$true,Position=1)]
[validatescript({ Test-Path $_ })]
[string]$DifferenceFolder,
[switch]$Recurse,
[switch]$Force,
[switch]$ShowDifferenceSide,
[switch]$ShowReferenceSide,
[switch]$ShowNewer
)
Begin {
# Clearing the default parameter values in the function's scope
$PSDefaultParameterValues.Clear()
}
Process {
$RefFolderFiles = Get-ChildItem -Path $ReferenceFolder -Recurse:$Recurse -Force:$Force -File
$DiffFolderFiles = Get-ChildItem -Path $DifferenceFolder -Recurse:$Recurse -Force:$Force -File
Write-Verbose "`$RefFolderFiles : $RefFolderFiles"
Write-Verbose "`$DiffFolderFiles : $DiffFolderFiles"
$RefFileHashes = $RefFolderFiles | Get-FileHash -Algorithm MD5
$DiffFileHashes = $DiffFolderFiles | Get-FileHash -Algorithm MD5
$UniqueFiles = Compare-Object $RefFileHashes $DiffFileHashes -Property Hash,Path
$Compare = Compare-Object $RefFileHashes $DiffFileHashes -Property Hash
If ($Compare -eq $null) {
Write-Verbose "Files in $DifferenceFolder are identical to files in $ReferenceFolder"
}
Else {
# Preparing an empty array to store each iteration of a custom difference object
$CustomDiffObj_Collection = @()
Foreach ($DiffObj in $Compare) {
$DiffObjWithPath = $UniqueFiles | Where-Object { $_.Hash -eq $DiffObj.Hash }
$DiffObjFile = Get-ChildItem -Path $DiffObjWithPath.Path
$DiffObjProperties = [ordered]@{'Name'=$DiffObjFile.Name
'Path'=$DiffObjFile.FullName
'LastEditTime'=$DiffObjFile.LastWriteTime
'Hash'=$DiffObj.Hash
'Folder'=if ($DiffObjFile.Directory.FullName -eq $ReferenceFolder -or $DiffObjFile.Directory.Parent.FullName -eq $ReferenceFolder -or $DiffObjFile.Directory.Parent.Parent.FullName -eq $ReferenceFolder) {"Reference"} Else {"Difference"}
}
# Building a custom object from the list of properties in $DiffObjProperties
$CustomDiffObj = New-Object -TypeName psobject -Property $DiffObjProperties
# Storing each $CustomDiffObj every time we go through the loop
$CustomDiffObj_Collection += $CustomDiffObj
}
}
}
End {
If ($PSBoundParameters.ContainsKey('ShowDifferenceSide') -and $ShowDifferenceSide -eq $True) {
$CustomDiffObj_Collection | Where-Object { $_.Folder -eq "Difference" }
}
ElseIf ($PSBoundParameters.ContainsKey('ShowReferenceSide') -and $ShowReferenceSide -eq $True) {
$CustomDiffObj_Collection | Where-Object { $_.Folder -eq "Reference" }
}
ElseIf ($PSBoundParameters.ContainsKey('ShowNewer') -and $ShowNewer -eq $True) {
If (-not ($ReferenceFolder.EndsWith("\") )) {
$ReferenceFolder += "\" }
If (-not ($DifferenceFolder.EndsWith("\") )) {
$DifferenceFolder += "\" }
Write-Verbose "`$ReferenceFolder : $ReferenceFolder"
Write-Verbose "`$DifferenceFolder : $DifferenceFolder"
# Preparing an empty array to store each iteration of final difference object
$FinalDiffObj_Collection = @()
Foreach ($FinalDiffObj in $CustomDiffObj_Collection) {
# Adding a property to identify the files with the same name and the same location within the reference and difference folder
$PathWithinFolder = if ($FinalDiffObj.Folder -eq "Reference") {
$FinalDiffObj.Path.Replace("$ReferenceFolder", "") } Else {
$FinalDiffObj.Path.Replace("$DifferenceFolder", "")}
$FinalDiffObj | Add-Member --MemberType NoteProperty --Name PathWithinFolder --Value $PathWithinFolder
$FinalDiffObj_Collection += $FinalDiffObj
}
# Grouping together the files with the same name and the same location within the reference and difference folder
$GroupedByPathWithinFolder = $FinalDiffObj_Collection | Group-Object -Property PathWithinFolder
Foreach ($FileGroup in $GroupedByPathWithinFolder) {
If ($FileGroup.Count -gt 1) {
if ( ($Filegroup.Group[0].LastEditTime) -ge ($Filegroup.Group[1].LastEditTime) ) {
$FileGroup.Group[0]
}
Else { $FileGroup.Group[1] }
}
Else { $Filegroup.Group }
}
}
Else {
# Sorting by name and path to make it easier to see the last version of a given file, if it is in both folders
$CustomDiffObj_Collection | Sort-Object -Property Name,Path
}
}
}