The other day I needed to know if the files in a specific directory structure on different systems were identical or different. Many tools exist for doing file and directory comparisons but the challenge here was that each system was locked-down, no installation of software was possible. And even worse, we needed to compare at least 3 different machines that couldn’t reach each others drives or shares. Instead of hunting around to find which one of the many tools would work in this environment I thought that Windows Powershell should be able to do this quite easily. In fact, it turned out to be so easy that just finding another tool would have taken much longer.

The script uses the Get-ChildItem cmdlet to recursively get each file/directory in the form of a System.IO.FileInfo or System.IO.DirectoryInfo object. We calculate the MD5 hash using the ComputeHash() method of the System.Security.Cryptography.MD5CryptoServiceProvider class. We simply write the hash and some interesting fields as a CSV string to the output.

Here’s the bits of code glued into a single script.

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
  {
     $hasher = new-object System.Security.Cryptography.MD5CryptoServiceProvider
  }
 
  process
  {
    $hashBytes = ''

    $item = Get-Item $InputObject -ErrorAction SilentlyContinue
 
    if ($item)
    {
      $InputObject = $item
    }
 
    if ($InputObject -is [System.IO.FileInfo])
    {
      $stream    = $null;
      $hashBytes = $null
 
      try
      {
        $stream     = $InputObject.OpenRead();
        $hashBytes  = $hasher.ComputeHash($stream);
      }
      finally
      {
        if ($stream -ne $null)
        {
          $stream.Close();
        }
      }
    }

 
    Write-Output ([BitConverter]::ToString($hashBytes)).Replace("-",'')
  }
}

function Get-FilesWithHash
{
    [CmdletBinding(SupportsShouldProcess=$false)]
    param
    (
        [string]$Directory,
        [switch]$Recurse
    )
 

    Get-ChildItem -Path $Directory -Recurse:$Recurse | foreach {
        $item = $_
        $hash = ''
        if (!$_.PSIsContainer) 
        {
            $hash = Get-MD5 $_    
        }
        else
        {
            $hash = ''
        }
        Write-Output "$($item.FullName),$hash,$($item.Length),$($item.CreationTime),$($item.LastWriteTime)" 
        
    }
}