Link to home
Start Free TrialLog in
Avatar of enthuguy
enthuguyFlag for Australia

asked on

How to get boolean result of md5sum from two files in powershell

I had this question after viewing md5sum check using powershell v 1.0.

Hi Experts,
this is an extension to my above question.

could you pls help how to loop thru a directory and get md5sum on each file and find the same file on another directory and run the md5sum and return true or false

Say directory1 has 3 files and directory2 has 10 files.
Loop through each file in directory1 get md5 and find same file in directory2 and get md5 too
Get a summary of each file to a output file

output.txt
file1 = success
file2 = success
file3 = failure (md5 value did not match)
Avatar of Carlo-Giuliani
Carlo-Giuliani
Flag of Canada image

Try something like this

$directory1 = '\\server\share\folder1'
$directory2 = '\\server\share\folder2'

$files = GCI $directory1  

ForEach ($f in $files) {
    $hash1 = Get-FileHash $f.fullname
    $hash2 = Get-FileHash (Join-Path $directory2 $f.name)
    Write-Host $f.name ($hash1 -eq $hash2)
    }

Open in new window

Avatar of David Johnson, CD
Function Get-FileHash
{
	[CmdletBinding(DefaultParameterSetName = 'Path')]
	param(
		[System.String[]]
		$Path,

		[Alias('PSPath')]
		[System.String[]]
		$LiteralPath,

		[System.String]
		$Algorithm='SHA256'
	)
	
	# Construct the strongly-typed crypto object
	$hasher = [System.Security.Cryptography.HashAlgorithm]::Create($Algorithm)
	
	$pathsToProcess = @()
	
	if($PSCmdlet.ParameterSetName  -eq 'LiteralPath')
	{
		$pathsToProcess += Resolve-Path -LiteralPath $LiteralPath
	}
	else
	{
		$pathsToProcess += Resolve-Path $Path
	}
	
	foreach($filePath in $pathsToProcess)
	{
		if(Test-Path -LiteralPath $filePath -PathType Container)
		{
			continue
		}
		
		try
		{
			# Read the file specified in $FilePath as a Byte array
			[system.io.stream]$stream = [system.io.file]::OpenRead($FilePath)

			# Compute file-hash using the crypto object
			[Byte[]] $computedHash = $hasher.ComputeHash($stream)
		}
		catch [Exception]
		{
			$errorMessage = [Microsoft.PowerShell.Commands.UtilityResources]::FileReadError -f $FilePath, $_
			Write-Error -Message $errorMessage -Category ReadError -ErrorId 'FileReadError' -TargetObject $FilePath
			return
		}
		finally
		{
			if($stream)
			{
				$stream.Close()
			}
		}
		
		# Convert to hex-encoded string
		[string] $hash = [BitConverter]::ToString($computedHash) -replace '-',''

		$retVal = [PSCustomObject] @{
			Algorithm = $Algorithm.ToUpperInvariant()
			Hash = $hash
			Path = $filePath
		}
		$retVal.psobject.TypeNames.Insert(0, 'Microsoft.Powershell.Utility.FileHash')

		$retVal
	}
}

$myobject = @()
$directory1 = read-host('Input Directory 1 Location')
if (!(test-path $directory1)) {
write-output ('invalid path:{0}' -f $directory1)
break
}
$directory2 = read-host('Input Directory 2 Location')
if (!(test-path $directory2)) {
write-output ('invalid path:{0}' -f $directory2)
break
}
if( $directory2[$directory2.Length -1] -ne '\'){
$directory2 += '\'
}
$files = get-childitem -path $directory1 -File
foreach ($file in $files){
$hash = Get-FileHash $file.fullname
$object = new-object -Typename psobject 
$object | add-member -MemberType NoteProperty -Name 'Filename' -Value $file.name
$object | add-member -MemberType NoteProperty -Name 'Hash' -Value $hash.Hash
$myobject += $object
}
$myobject2 = @()
foreach ($file in $myobject)
{

$testfile = $directory2 + $file.filename
if(!(Test-path $testfile)) {
write-output ('{0} not found' -f $testfile)
break
}
else {
$successful = '= Success'
$hash = Get-FileHash $testfile
if ($file.hash -ne $hash.hash){
$successful = '= Failure (md5 value not match)'
}
write-output('{0} = {1}' -f $file.fileName,$successful)
$object = new-object -Typename psobject 
$object | add-member -MemberType NoteProperty -Name 'Filename' -Value $file.Filename
$object | add-member -MemberType NoteProperty -Name 'Success' -Value $successful
$myobject2 += $object
}
$myobject2 | out-file c:\test2\ee.txt

}

Open in new window

Avatar of enthuguy

ASKER

Thanks Carlo, that looks neat but I believe 1.0 doesn't support this

thanks David, I will try tonight, as I'm away from my pc. btw how should call this function? how to pass the two directory as parameter  (sorry I'm sort of new to powershell)
it will ask you.. read-host gets input from standard input which in this case is the keyboard
Using the same function from the other post, I would keep it simple create 2 arrays with a list of files and hashes and then use compare-object to find the differences.

Function Get-FileHash
{
	[CmdletBinding(DefaultParameterSetName = "Path")]
	param(
		[System.String[]]
		$Path,

		[Alias("PSPath")]
		[System.String[]]
		$LiteralPath,

		[System.String]
		$Algorithm="SHA256"
	)
	
	# Construct the strongly-typed crypto object
	$hasher = [System.Security.Cryptography.HashAlgorithm]::Create($Algorithm)
	
	$pathsToProcess = @()
	
	if($PSCmdlet.ParameterSetName  -eq "LiteralPath")
	{
		$pathsToProcess += Resolve-Path -LiteralPath $LiteralPath
	}
	else
	{
		$pathsToProcess += Resolve-Path $Path
	}
	
	foreach($filePath in $pathsToProcess)
	{
		if(Test-Path -LiteralPath $filePath -PathType Container)
		{
			continue
		}
		
		try
		{
			# Read the file specified in $FilePath as a Byte array
			[system.io.stream]$stream = [system.io.file]::OpenRead($FilePath)

			# Compute file-hash using the crypto object
			[Byte[]] $computedHash = $hasher.ComputeHash($stream)
		}
		catch [Exception]
		{
			$errorMessage = [Microsoft.PowerShell.Commands.UtilityResources]::FileReadError -f $FilePath, $_
			Write-Error -Message $errorMessage -Category ReadError -ErrorId "FileReadError" -TargetObject $FilePath
			return
		}
		finally
		{
			if($stream)
			{
				$stream.Close()
			}
		}
		
		# Convert to hex-encoded string
		[string] $hash = [BitConverter]::ToString($computedHash) -replace '-',''

		$retVal = [PSCustomObject] @{
			Algorithm = $Algorithm.ToUpperInvariant()
			Hash = $hash
			Path = $filePath
		}
		$retVal.psobject.TypeNames.Insert(0, "Microsoft.Powershell.Utility.FileHash")

		$retVal
	}
}

$arr1 = gci -Recurse c:\temp\folder1 | % {
	Get-FileHash $_.fullname} | select `
		@{n="Hash";e={$_.item("hash")}}, `
		@{n="algorithm";e={$_.item("algorithm")}}, `
		@{n="Path";e={$_.item("path")}
}

$arr2 = gci -Recurse c:\temp\folder2 | % {
	Get-FileHash $_.fullname} | select `
		@{n="Hash";e={$_.item("hash")}}, `
		@{n="algorithm";e={$_.item("algorithm")}}, `
		@{n="Path";e={$_.item("path")}
}

Compare-Object -ReferenceObject $arr1 -DifferenceObject $arr2 -Property hash | % {
	$indicator = $_
	if ($indicator.sideindicator -eq "<=")
	{
		$arr1 | ? {$_.hash -eq $indicator.hash}
	}
	else
	{
		$arr2 | ? {$_.hash -eq $indicator.hash}
	}
}

Open in new window


I find Compare-Object a simple way to compare arrays.
Hi David,
Thanks very much...that worked fine for me, I will attempt how to pass two directories as arguments to that function.

@Learnctx,
I changed the path in the function but I rece. below blank report. I'm sure I did something wrong...sorry :(

PS C:\parent\scripts> .\compareMD52.ps1

Hash                                                        algorithm                                                   Path
----                                                        ---------                                                   ----
ASKER CERTIFIED SOLUTION
Avatar of David Johnson, CD
David Johnson, CD
Flag of Canada image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks very much!!