-
-
Save kimboslice99/c8206c1e1c4044e19634bba603182dab to your computer and use it in GitHub Desktop.
[PowerShell 7] Get-HashEx - All-in-one file and folder hashing module for Windows PowerShell 7.0+
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
.Synopsis | |
Hash a file or a folder via the lifestrea- er I mean, bytestream. Then return the results. | |
.Description | |
I improved Get-FileHash just a bit. Hash a file or a folder via the lifestrea- er I mean, | |
bytestream. Then returns the results. A lot of options, and what ever supported, hardware | |
accelerated CryptoStream HashAlgorithm provider you want. Also with real time progress, | |
which you can turn off if you want. | |
FEATURES: | |
a. Get cryptographic hash of single file with progress. | |
b. Custom functionality to recursively hash contents of entire folders | |
c. Combined hash for entire folder via recursive hashing. | |
d. Uses the CryptoStream and HashAlgorithm provider for speed. | |
.Parameter Path | |
The path to the file or folder to compute a hash for. It can be a relative | |
or an absolute path. | |
.Parameter Algorithm | |
One of System.Cryptography.HashAlgorithm options, as a string. | |
.Parameter BufferSize | |
Size of the stream buffer in bytes, can perform better depending on your disk. | |
Default is 1mb (1048576 bytes), which is good for most SSDs. For HDDs, you | |
should set this to your block size to align reads and writes correctly for | |
efficient streaming / protect drive brakes from wear. Keep your blocks | |
aligned to the cylander for best results when using System.IO.Stream asyncronous | |
functions. | |
.Parameter Combine | |
Optionally specify this when -Folder switch is also specified. It will produce a single | |
entry containing folder properties and the accumulated hash (Transformed hash) | |
.Parameter NoProgress | |
Optionally specify this flag to surpress progress output. | |
.Parameter Folder | |
Optionally specify this flag to indicate that the Path parameter specified is a folder | |
that should be recursively hashed. | |
#> | |
Function Get-HashEx() { | |
param( | |
[Parameter(Mandatory = $true, Position = 0)][string]$Path, | |
[Parameter(Mandatory = $false)][string]$Algorithm = "SHA256", | |
[Parameter(Mandatory = $false)][int64]$BufferSize = 1mb, | |
[Parameter(Mandatory = $false)][switch]$Combine = $false, | |
[Parameter(Mandatory = $false)][switch]$NoProgress = $false, | |
[Parameter(Mandatory = $false)][switch]$Folder = $false | |
) | |
begin { | |
if ($Combine -And $Folder) { | |
$AlgorithmObj = [System.Security.Cryptography.HashAlgorithm]::Create($Algorithm); | |
$CryptoStream = [System.Security.Cryptography.CryptoStream]::new(([System.IO.Stream]::Null), $AlgorithmObj, "Write"); | |
} elseif (-Not $Folder) { | |
$AlgorithmObj = [System.Security.Cryptography.HashAlgorithm]::Create($Algorithm); | |
$CryptoStream = [System.Security.Cryptography.CryptoStream]::new(([System.IO.Stream]::Null), $AlgorithmObj, "Write"); | |
}; | |
$buffer = New-Object Byte[] $BufferSize; | |
if ((-Not $NoProgress) -And $Folder) { | |
$filemeasures = (Get-ChildItem $Path -Recurse -Attributes !Directory,!Directory+Hidden | Measure-Object -Property Length -Sum); | |
$filecount = $filemeasures.Count; | |
$filesize = $filemeasures.Sum; | |
$filemeasures = $null; | |
$processedfiles = 0; | |
$FT = @() | |
} elseif ($NoProgress -And $Folder) { | |
$FT = @() | |
}; | |
$Error = $false; | |
} | |
process { | |
try { | |
if ($Folder) { | |
Get-ChildItem $Path -Recurse -Attributes !Directory,!Directory+Hidden -ErrorAction SilentlyContinue |% { | |
$myfileobj = $_; | |
# Display progress | |
if (-Not $NoProgress) { | |
$prog = ($processedfiles / $filecount); | |
Write-Progress -Id 1 -Activity "Get-HashEx is running..." -Status ("Progress: {0:P2}" -f $prog) -PercentComplete ($prog * 100) -CurrentOperation $myfileobj.FullName; | |
$processedfiles++; | |
}; | |
# If we are not combining items, we need to initialize a fresh CryptoStream | |
# each iteration. | |
if (-Not $Combine) { | |
$AlgorithmObj = [System.Security.Cryptography.HashAlgorithm]::Create($Algorithm); | |
$CryptoStream = [System.Security.Cryptography.CryptoStream]::new(([System.IO.Stream]::Null), $AlgorithmObj, "Write"); | |
} | |
# Stream the file to buffer and into CryptoStream. | |
$FileStream = [System.IO.File]::OpenRead($_.FullName); | |
while ( $bytesRead = $FileStream.Read($buffer, 0, $BufferSize) ){ | |
# Display progress for Get-FileHash by tracking FileStream position. | |
if ((-Not $NoProgress)) { | |
$prog2 = ($FileStream.Position / $FileStream.Length); | |
Write-Progress -Id 2 -Activity "CryptoStream is running..." -Status ("Progress: {0:P2}" -f ($prog2)) -PercentComplete ($prog2 * 100) -CurrentOperation ("{0} of {1} bytes hashed" -f $FileStream.Position, $FileStream.Length); | |
} | |
# Write to the Stream from the buffer and then flush the CryptoStream block queue. | |
$CryptoStream.Write($buffer, 0, $bytesRead); | |
$CryptoStream.Flush(); | |
} | |
$FileStream.Close(); $FileStream.Dispose(); | |
# If we are not combining items, finalize the CryptoStream, store the result into | |
# the table, and then dispose of the CryptoStream and HashAlgorithm provider | |
# we used in this iteration. | |
if (-Not $Combine) { | |
$CryptoStream.FlushFinalBlock(); | |
$myfilehash = $(($AlgorithmObj.Hash | ForEach-Object {$_.ToString("X2")}) -join ''); | |
$myrelativename = ($myfileobj.FullName).Replace([System.IO.Path]::GetFullPath($Path), ".\") | |
$mylastwritetime = [int64]((New-TimeSpan -Start ([timezone]::CurrentTimeZone.ToLocalTime([datetime]'1/1/1970')) -End $myfileobj.LastWriteTime).TotalMilliseconds) | |
$mysize = ($myfileobj.Length) | |
$item = [PSCustomObject]@{ | |
FullName = ($myfileobj.FullName) | |
RelativeName = $myrelativename | |
Size = $mysize | |
LastWriteTime = $mylastwritetime | |
Hash = $myfilehash | |
}; | |
$FT += $item; | |
$CryptoStream.Close(); $CryptoStream.Dispose(); $AlgorithmObj.Dispose(); | |
} else { | |
$CryptoStream.Flush(); | |
} | |
} | |
# After everything has completed, if we are combining items, finalize the CryptoStream, | |
# and store the folder properties as a single entry in the table, then destroy the | |
# CryptoStream and the HashAlgorithm provider. | |
if ($Combine) { | |
$CryptoStream.FlushFinalBlock(); | |
$myfileobj = $(Get-Item -Path $([System.IO.Path]::GetFullPath($Path)) -Force); | |
$myfilehash = $(($AlgorithmObj.Hash | ForEach-Object {$_.ToString("X2")}) -join ''); | |
$myrelativename = ($myfileobj.FullName).Replace([System.IO.Path]::GetFullPath($Path), ".\") | |
$mylastwritetime = [int64]((New-TimeSpan -Start ([timezone]::CurrentTimeZone.ToLocalTime([datetime]'1/1/1970')) -End $myfileobj.LastWriteTime).TotalMilliseconds) | |
[int64]$mysize = [int64]((Get-ChildItem $Path -Recurse -Attributes !Directory,!Directory+Hidden | Measure-Object -Property Length -Sum).Sum); | |
$item = [PSCustomObject]@{ | |
FullName = ($myfileobj.FullName) | |
RelativeName = $myrelativename | |
Size = $mysize | |
LastWriteTime = $mylastwritetime | |
Hash = $myfilehash | |
}; | |
$FT += $item; | |
$CryptoStream.Close(); $CryptoStream.Dispose(); $AlgorithmObj.Dispose(); | |
} | |
} else { | |
# Stream the file to buffer and into CryptoStream. | |
$FileStream = [System.IO.File]::OpenRead([System.IO.Path]::GetFullPath($Path)); | |
while ( $bytesRead = $FileStream.Read($buffer, 0, $BufferSize) ){ | |
# Display progress for Get-FileHash by tracking FileStream position. | |
if ((-Not $NoProgress)) { | |
$prog2 = ($FileStream.Position / $FileStream.Length); | |
Write-Progress -Id 2 -Activity "Get-HashEx is running..." -Status ("Progress: {0:P2}" -f ($prog2)) -PercentComplete ($prog2 * 100) -CurrentOperation ("{0} of {1} bytes hashed" -f $FileStream.Position, $FileStream.Length); | |
} | |
# Write to the Stream from the buffer and then flush the CryptoStream block queue. | |
$CryptoStream.Write($buffer, 0, $bytesRead); | |
$CryptoStream.Flush(); | |
} | |
$FileStream.Close(); $FileStream.Dispose(); | |
# Finalize the CryptoStream, store the result into the table, and then dispose of | |
# the CryptoStream and HashAlgorithm provider. | |
$CryptoStream.FlushFinalBlock(); | |
$myfileobj = $(Get-Item -LiteralPath $([System.IO.Path]::GetFullPath($Path)) -Force) | |
$myfilehash = $(($AlgorithmObj.Hash | ForEach-Object {$_.ToString("X2")}) -join ''); | |
$myrelativename = ($myfileobj.FullName).Replace([System.IO.Path]::GetFullPath($Path), ".\") | |
$mylastwritetime = [int64]((New-TimeSpan -Start ([timezone]::CurrentTimeZone.ToLocalTime([datetime]'1/1/1970')) -End $myfileobj.LastWriteTime).TotalSeconds) # Make timestamp UNIX epoch | |
$mysize = ($myfileobj.Length) | |
$FT = [PSCustomObject]@{ | |
FullName = ($myfileobj.FullName) | |
RelativeName = $myrelativename | |
Size = $mysize | |
LastWriteTime = $mylastwritetime | |
Hash = $myfilehash | |
}; | |
$FileStream.Close(); $FileStream.Dispose(); | |
$CryptoStream.Close(); $CryptoStream.Dispose(); $AlgorithmObj.Dispose(); | |
} | |
} catch { | |
Write-Error "$($_.Exception.Message)`n`nStack trace: $($_.Exception.StackTrace)"; | |
$Error = $true; | |
} | |
} | |
end { | |
if (($Combine -And ($Error -Eq $false)) -Or ((-Not $Combine) -And ($Error -Eq $false))) { | |
$result = $FT; | |
} else { | |
$result = $null; | |
} | |
$result | |
} | |
} | |
Export-Module -Function Get-HashEx |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment