Last active
January 18, 2026 05:12
-
-
Save iOnline247/d1a69bfaa7d07a2d5a55d0e8620ce483 to your computer and use it in GitHub Desktop.
Checksum the checksums!
This file contains hidden or 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
| [CmdletBinding()] | |
| param ( | |
| [Parameter(Mandatory = $true)] | |
| [string]$LiteralPath, | |
| [Parameter(Mandatory = $false)] | |
| [string]$CompareCsvPath, | |
| [Parameter(Mandatory = $false)] | |
| [ValidateRange(1, 64)] | |
| [int]$ThrottleLimit = 8, | |
| [Parameter(Mandatory = $false)] | |
| [ValidateSet("SHA256", "SHA1", "MD5", "SHA384", "SHA512")] | |
| [string]$Algorithm = "SHA256", | |
| [Parameter(Mandatory = $false)] | |
| [string]$CSVDirectory | |
| ) | |
| if (-not (Test-Path $LiteralPath)) { | |
| throw "Path not found: $LiteralPath" | |
| } | |
| if ($CompareCsvPath -and -not (Test-Path $CompareCsvPath)) { | |
| throw "Compare CSV not found: $CompareCsvPath" | |
| } | |
| $sep = [System.IO.Path]::DirectorySeparatorChar | |
| $CmdletName = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) | |
| if ([string]::IsNullOrEmpty($CSVDirectory)) { | |
| $CSVDirectory = Join-Path ([System.IO.Path]::GetTempPath()) $CmdletName | |
| } | |
| if (-not (Test-Path $CSVDirectory)) { | |
| New-Item -Path $CSVDirectory -ItemType Directory | Out-Null | |
| } | |
| $Algorithm = $Algorithm.ToLower(); | |
| $Timestamp = Get-Date -Format "yyyyMMdd_HHmmss" | |
| $Guid = [guid]::NewGuid().ToString() | |
| $fileLedgerCsvPath = Join-Path $CSVDirectory "checksums_${Algorithm}_${Timestamp}_${Guid}.csv" | |
| $BasePath = (Resolve-Path $LiteralPath).Path.TrimEnd($sep) | |
| function Write-LfCsv { | |
| [CmdletBinding()] | |
| param ( | |
| [Parameter(Mandatory = $true)] | |
| [string]$Path, | |
| [Parameter(Mandatory = $false)] | |
| [switch]$NoBom | |
| ) | |
| if (-not (Test-Path $Path)) { | |
| throw "CSV not found: $Path" | |
| } | |
| $content = Get-Content -Path $Path -Raw | |
| $content = $content -replace "`r`n", "`n" | |
| if ($NoBom) { | |
| $utf8NoBom = [System.Text.UTF8Encoding]::new($false) | |
| [System.IO.File]::WriteAllText($Path, $content, $utf8NoBom) | |
| } | |
| else { | |
| Set-Content -Path $Path -Value $content -NoNewline -Encoding UTF8 | |
| } | |
| } | |
| function Get-ChecksumData { | |
| param ( | |
| [Parameter(Mandatory)] | |
| [string]$BaseDirectory, | |
| [Parameter(Mandatory)] | |
| [int]$Throttle, | |
| [Parameter(Mandatory)] | |
| [string]$HashAlgorithm | |
| ) | |
| $files = Get-ChildItem -Path $BaseDirectory -Recurse -File | |
| $total = $files.Count | |
| Write-Host "π’ Total files to process: $total" | |
| $files | ForEach-Object -Parallel { | |
| $hash = Get-FileHash -LiteralPath $_.FullName -Algorithm $using:HashAlgorithm | |
| $sep = [System.IO.Path]::DirectorySeparatorChar | |
| $relPath = $_.FullName.Substring($using:BaseDirectory.Length).TrimStart($sep) | |
| [pscustomobject]@{ | |
| RelativePath = $relPath | |
| FileName = $_.Name | |
| Algorithm = $using:HashAlgorithm | |
| Hash = $hash.Hash.ToLower() | |
| } | |
| } -ThrottleLimit $Throttle | Sort-Object RelativePath | |
| } | |
| # Main Execution | |
| Write-Host "π Scanning directory (PARALLEL=$ThrottleLimit, Algorithm=$Algorithm): $BasePath" | |
| $directoryChecksumData = Get-ChecksumData -BaseDirectory $BasePath -Throttle $ThrottleLimit -HashAlgorithm $Algorithm | |
| $directoryChecksumData | Export-Csv -Path $fileLedgerCsvPath -NoTypeInformation -Encoding UTF8 | |
| # Normalize to LF-only line endings and write without BOM | |
| Write-LfCsv -Path $fileLedgerCsvPath -NoBom | |
| Write-Host "β File Ledger CSV generated:" | |
| Write-Host " $fileLedgerCsvPath" | |
| $newHash = (Get-FileHash -LiteralPath $fileLedgerCsvPath -Algorithm $Algorithm).Hash.ToLower() | |
| # Verify against existing CSV if provided | |
| if ($CompareCsvPath) { | |
| $oldHash = (Get-FileHash -LiteralPath $CompareCsvPath -Algorithm $Algorithm).Hash.ToLower() | |
| if ($oldHash -eq $newHash) { | |
| Write-Host "β CSV HASH MATCH β DATA IDENTICAL" -ForegroundColor Green | |
| } | |
| else { | |
| Write-Host "β CSV HASH MISMATCH" -ForegroundColor Red | |
| Write-Host " Expected: $oldHash" | |
| Write-Host " Actual: $newHash" | |
| } | |
| } | |
| # Output the new hash to the pipeline | |
| $newHash |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example Usages: