This commit is contained in:
2026-09-25 15:16:30 -06:00
parent b010b7ac71
commit 53fe4e9ed3
2 changed files with 224 additions and 210 deletions
+218 -119
View File
@@ -7,28 +7,33 @@ param(
# Output file to save generated checksums (-o mode) # Output file to save generated checksums (-o mode)
[Parameter(Mandatory = $false, ParameterSetName = 'Generate')] [Parameter(Mandatory = $false, ParameterSetName = 'Generate')]
[Alias('o')] [Alias('o', '-outfile')]
[string]$OutFile, [string]$OutFile,
# File containing checksums to verify (-c mode) # File containing checksums to verify (-c mode)
[Parameter(Mandatory = $false, ParameterSetName = 'Verify')] [Parameter(Mandatory = $false, ParameterSetName = 'Verify')]
[Alias('c')] [Alias('c', '-check')]
[string]$Check, [string]$Check,
# Direct checksum hash string to match against (-m mode) # Direct checksum hash string to match against (-m mode)
[Parameter(Mandatory = $true, ParameterSetName = 'Match')] [Parameter(Mandatory = $true, ParameterSetName = 'Match')]
[Alias('m')] [Alias('m', '-match')]
[string]$Match, [string]$Match,
# Hashing algorithm to use (Defaults to SHA256) # Hashing algorithm to use (Defaults to SHA256)
[Parameter(Mandatory = $false)] [Parameter(Mandatory = $false)]
[Alias('a')] [Alias('a', 'algo', '-algo', '-algorithm')]
[ValidateSet('SHA1', 'SHA256', 'SHA384', 'SHA512', 'MD5')] [ValidateSet('SHA1', 'SHA256', 'SHA384', 'SHA512', 'MD5')]
[string]$Algorithm, [string]$Algorithm,
# Help flag (-h, -Help) # Recursive flag (-r, -recursive, --recursive)
[Parameter(Mandatory = $false)] [Parameter(Mandatory = $false)]
[Alias('h')] [Alias('r', 'recursive', '-recursive')]
[switch]$Recursive,
# Help flag (-h, -Help, --Help, --H)
[Parameter(Mandatory = $false)]
[Alias('h', '-h', '-help')]
[switch]$Help [switch]$Help
) )
@@ -39,35 +44,47 @@ NAME
checksum - Compute and verify message digests / file checksums checksum - Compute and verify message digests / file checksums
SYNOPSIS SYNOPSIS
.\checksum.ps1 [-FilePath] <string[]> [-OutFile <string>] [-Algorithm <string>] .\checksum.ps1 [-FilePath] <string[]> [-OutFile <string>] [-Algorithm <string>] [-Recursive]
.\checksum.ps1 [-FilePath] <string[]> -Match <string> [-Algorithm <string>] .\checksum.ps1 [-FilePath] <string[]> -Match <string> [-Algorithm <string>] [-Recursive]
.\checksum.ps1 -Check <string> [-Algorithm <string>] .\checksum.ps1 -Check <string> [-Algorithm <string>] [-Recursive]
.\checksum.ps1 -Help .\checksum.ps1 -Help
DESCRIPTION DESCRIPTION
Computes or verifies checksums using various cryptographic algorithms. Computes or verifies checksums using various cryptographic algorithms.
Outputs hash values in standard Linux utility format (<hash> <filename>). Outputs hash values in standard Linux utility format (<hash> <filename>).
Ignores checksum files (.md5, .sha1, .sha256, .sha384, .sha512) unless explicitly passed.
NOTE: All flags and parameters are completely case-insensitive
(e.g., -r, -R, --Recursive, and --recursive are treated identically).
PARAMETERS PARAMETERS
-FilePath <string[]> -FilePath <string[]>
Specifies the path to one or more files to hash. Accepts wildcards (*). Specifies the path to one or more files to hash. Accepts wildcards (*).
-o, -OutFile <string> -o, -OutFile, --OutFile <string>
Specifies an output file to save the checksum results to, while still Specifies an output file to save the checksum results to.
displaying the output on screen. If passed as an empty string (e.g. -o ""), the file is auto-named:
- If input has a wildcard (*), outputs to 'checksums.<algo>'
- If no wildcard is used, outputs to '<filename>.<algo>' per file.
When combined with -r and *, it auto-generates checksum files
inside the current working directory and each subdirectory recursively.
-c, -Check <string> -r, -Recursive, --Recursive <switch>
Recursively generate or verify checksums in subdirectories.
-c, -Check, --Check <string>
Read checksums from the specified file and verify them against files on disk. Read checksums from the specified file and verify them against files on disk.
If -r is used, searches for the specified filename recursively in subdirectories.
-m, -Match <string> -m, -Match, --Match <string>
Compare the computed hash of the target file(s) against a specific hash string. Compare the computed hash of the target file(s) against a specific hash string.
-a, -Algorithm <string> -a, -Algorithm, --Algorithm, -Algo, --Algo <string>
Specifies the cryptographic hash algorithm to use. Specifies the cryptographic hash algorithm to use.
Supported values: SHA1, SHA256 (default), SHA384, SHA512, MD5. Supported values: SHA1, SHA256 (default), SHA384, SHA512, MD5.
If omitted in -c or -m mode, the algorithm is auto-detected by hash length. If omitted in -c or -m mode, the algorithm is auto-detected by hash length.
-h, -Help -h, -Help, --Help, --H <switch>
Display this help message. Display this help message.
EXAMPLES EXAMPLES
@@ -75,13 +92,13 @@ EXAMPLES
.\checksum.ps1 myfile.iso .\checksum.ps1 myfile.iso
2. Compute checksums for all files and write to a file while showing output: 2. Compute checksums for all files and write to a file while showing output:
.\checksum.ps1 * -o checksums.sha256 .\checksum.ps1 * --OutFile checksums.sha256
3. Verify a file against a explicit hash string: 3. Auto-generate per-directory checksum files recursively for all files:
.\checksum.ps1 myfile.iso -m "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" .\checksum.ps1 * -r -o ""
4. Verify checksums from a verification file (auto-detects algorithm): 4. Verify checksums from verification files recursively:
.\checksum.ps1 -c checksums.sha256 .\checksum.ps1 --Check checksums.sha256 --Recursive
'@ '@
} }
@@ -105,9 +122,9 @@ EXAMPLES
process { process {
# Match help arguments passed either as switch (-h, -Help) or positional string ('help', 'h', '--help', etc.) # Match help arguments passed either as switch (-h, -Help) or positional string ('help', 'h', '--help', etc.)
$isHelpRequested = $Help.IsPresent -or ( $isHelpRequested =$Help.IsPresent -or (
$FilePath -and $FilePath.Count -eq 1 -and ( $FilePath -and$FilePath.Count -eq 1 -and (
$FilePath[0] -match '^(?i)[-/]{0,2}(h|help)$' $FilePath[0] -match '^(?i)[-/]{0,2}(h\vert{}help)$'
) )
) )
@@ -118,17 +135,59 @@ process {
# Normalize explicit algorithm input if provided # Normalize explicit algorithm input if provided
if (-not [string]::IsNullOrWhiteSpace($Algorithm)) { if (-not [string]::IsNullOrWhiteSpace($Algorithm)) {
$Algorithm = $Algorithm.ToUpper() $Algorithm =$Algorithm.ToUpper()
}
# --- ITEM GATHERING (Generate & Match Modes) ---
if ($PSCmdlet.ParameterSetName -in @('Generate', 'Match')) {
if (-not $FilePath -or$FilePath.Count -eq 0) {
Write-Error "checksum: Missing file path parameter. Use -Help for usage."
return
}
$allItems = @()$isWildcardGlobal = $false$skipExtensions = @('.md5', '.sha1', '.sha256', '.sha384', '.sha512')
foreach ($pathEntry in$FilePath) {
$isWildcard = ($pathEntry -match '\*')
if ($isWildcard) { $isWildcardGlobal =$true }
$resolvedItems = @()
if ($Recursive) {
$resolvedItems = Get-ChildItem -Path$pathEntry -Recurse -File -ErrorAction SilentlyContinue
} else {
if ($isWildcard -or ($pathEntry -match '\?')) {
$resolvedItems = Get-ChildItem -Path$pathEntry -File -ErrorAction SilentlyContinue
} else {
$resolvedItems = Get-Item -Path$pathEntry -ErrorAction SilentlyContinue
}
}
if (-not $resolvedItems) {
Write-Error "checksum: $pathEntry`: No such file or directory"
continue
}
$explicitFileFound = $false
if ((-not $isWildcard) -and (Test-Path -Path $pathEntry -PathType Leaf)) {
$explicitFileFound = $true
}
foreach ($item in $resolvedItems) {
if ($item.PSIsContainer) { continue }
# Feature 1: Skip checksum extension files unless they were named explicitly
if (-not $explicitFileFound) {
if ($skipExtensions -contains $item.Extension.ToLower()) {
continue
}
}
$allItems += $item
}
}
} }
# --- DIRECT MATCH MODE (-m / -Match) --- # --- DIRECT MATCH MODE (-m / -Match) ---
if ($PSCmdlet.ParameterSetName -eq 'Match') { if ($PSCmdlet.ParameterSetName -eq 'Match') {
if (-not $FilePath -or $FilePath.Count -eq 0) {
Write-Error "checksum: Missing file path parameter for matching. Use -Help for usage."
return
}
# Auto-detect algorithm from provided -Match hash string if not explicitly given
if ([string]::IsNullOrWhiteSpace($Algorithm)) { if ([string]::IsNullOrWhiteSpace($Algorithm)) {
$Algorithm = Get-AlgorithmFromHashLength -HashString $Match $Algorithm = Get-AlgorithmFromHashLength -HashString $Match
if (-not $Algorithm) { return } if (-not $Algorithm) { return }
@@ -137,140 +196,180 @@ process {
$expectedHash = $Match.Trim().ToLower() $expectedHash = $Match.Trim().ToLower()
foreach ($pathEntry in $FilePath) { foreach ($item in $allItems) {
$resolvedItems = Get-Item -Path $pathEntry -ErrorAction SilentlyContinue $actualHash = (Get-FileHash -Path $item.FullName -Algorithm $Algorithm).Hash.ToLower()
$targetFile = Resolve-Path -Path $item.FullName -Relative
if ($targetFile.StartsWith(".\")) { $targetFile = $targetFile.Substring(2) }
if (-not $resolvedItems) { if ($actualHash -eq $expectedHash) {
Write-Error "checksum: $pathEntry`: No such file or directory" Write-Host "${targetFile}: " -NoNewline
continue Write-Host "OK" -ForegroundColor Green
} } else {
Write-Host "${targetFile}: " -NoNewline
foreach ($item in $resolvedItems) { Write-Host "FAILED" -ForegroundColor Red
if ($item.PSIsContainer) {
Write-Error "checksum: $($item.Name)`: Is a directory"
} else {
$actualHash = (Get-FileHash -Path $item.FullName -Algorithm $Algorithm).Hash.ToLower()
$targetFile = Resolve-Path -Path $item.FullName -Relative
if ($targetFile.StartsWith(".\")) { $targetFile = $targetFile.Substring(2) }
if ($actualHash -eq $expectedHash) {
Write-Host "${targetFile}: " -NoNewline
Write-Host "OK" -ForegroundColor Green
} else {
Write-Host "${targetFile}: " -NoNewline
Write-Host "FAILED" -ForegroundColor Red
}
}
} }
} }
} }
# --- VERIFICATION MODE (-c / -Check) --- # --- VERIFICATION MODE (-c / -Check) ---
elseif ($PSCmdlet.ParameterSetName -eq 'Verify') { elseif ($PSCmdlet.ParameterSetName -eq 'Verify') {
if (-not (Test-Path -Path $Check -PathType Leaf)) { $checkFiles = @()
if ($Recursive) {
# Find all files matching the name of $Check recursively
$checkFileName = Split-Path $Check -Leaf
$checkFiles = Get-ChildItem -Path .\ -Filter $checkFileName -Recurse -File -ErrorAction SilentlyContinue
} else {
if (Test-Path -Path $Check -PathType Leaf) {
$checkFiles = @(Get-Item -Path $Check)
}
}
if (-not $checkFiles -or $checkFiles.Count -eq 0) {
Write-Error "checksum: $Check`: No such file or directory" Write-Error "checksum: $Check`: No such file or directory"
return return
} }
$lines = Get-Content -Path $Check foreach ($cf in$checkFiles) {
$lines = Get-Content -Path$cf.FullName
$baseDir =$cf.DirectoryName
# Auto-detect algorithm based on hash length if not explicitly provided $fileAlgo =$Algorithm
if ([string]::IsNullOrWhiteSpace($Algorithm)) { if ([string]::IsNullOrWhiteSpace($fileAlgo)) {
foreach ($line in $lines) { foreach ($line in$lines) {
if ([string]::IsNullOrWhiteSpace($line) -or $line.StartsWith("#")) { continue } if ([string]::IsNullOrWhiteSpace($line) -or$line.StartsWith("#")) { continue }
if ($line -match '^([a-fA-F0-9]+)\s+') { if ($line -match '^([a-fA-F0-9]+)\s+') {
$Algorithm = Get-AlgorithmFromHashLength -HashString $Matches[1] $fileAlgo = Get-AlgorithmFromHashLength -HashString$Matches[1]
if (-not $Algorithm) { return } if ($fileAlgo) { Write-Verbose "Auto-detected algorithm: $fileAlgo in $($cf.Name)" }
Write-Verbose "Auto-detected algorithm: $Algorithm" break
break }
} }
if ([string]::IsNullOrWhiteSpace($fileAlgo)) {$fileAlgo = 'SHA256' }
} }
# Fallback if file contained no parsable hashes foreach ($line in$lines) {
if ([string]::IsNullOrWhiteSpace($Algorithm)) { if ([string]::IsNullOrWhiteSpace($line) -or$line.StartsWith("#")) { continue }
$Algorithm = 'SHA256'
}
}
foreach ($line in $lines) { if ($line -match '^([a-fA-F0-9]+)\s+[* ]?(.*)$') {
# Skip empty lines or comments $expectedHash =$Matches[1].ToLower()
if ([string]::IsNullOrWhiteSpace($line) -or $line.StartsWith("#")) { continue } $targetFile =$Matches[2].Trim().TrimStart('*')
# Match standard checksum output format: "<hash> [* ]<filename>" # Resolve path relative to where the check file currently resides
if ($line -match '^([a-fA-F0-9]+)\s+[* ]?(.*)$') { $targetFullPath = Join-Path -Path $baseDir -ChildPath$targetFile
$expectedHash = $Matches[1].ToLower()
# Strip leading whitespace and any leftover leading asterisk
$targetFile = $Matches[2].Trim().TrimStart('*')
if (Test-Path -Path $targetFile -PathType Leaf) { if (Test-Path -Path $targetFullPath -PathType Leaf) {$actualHash = (Get-FileHash -Path $targetFullPath -Algorithm$fileAlgo).Hash.ToLower()
$actualHash = (Get-FileHash -Path $targetFile -Algorithm $Algorithm).Hash.ToLower()
if ($actualHash -eq $expectedHash) { $displayPath = Resolve-Path -Path$targetFullPath -Relative
Write-Host "${targetFile}: " -NoNewline if ($displayPath.StartsWith(".\")) { $displayPath =$displayPath.Substring(2) }
Write-Host "OK" -ForegroundColor Green
if ($actualHash -eq$expectedHash) {
Write-Host "${displayPath}: " -NoNewline
Write-Host "OK" -ForegroundColor Green
} else {
Write-Host "${displayPath}: " -NoNewline
Write-Host "FAILED" -ForegroundColor Red
}
} else { } else {
Write-Host "${targetFile}: " -NoNewline # Format missing path nicely
Write-Host "FAILED" -ForegroundColor Red $displayPath =$targetFile
if ($baseDir -ne (Get-Location).Path) {$displayPath = Join-Path -Path (Resolve-Path -Path $baseDir -Relative) -ChildPath$targetFile
if ($displayPath.StartsWith(".\")) { $displayPath =$displayPath.Substring(2) }
}
Write-Host "${displayPath}: " -NoNewline
Write-Host "FAILED open or read" -ForegroundColor Red
} }
} else { } else {
Write-Host "${targetFile}: " -NoNewline Write-Warning "checksum: improperly formatted line in $($cf.Name):$line"
Write-Host "FAILED open or read" -ForegroundColor Red
} }
} else {
Write-Warning "checksum: improperly formatted line: $line"
} }
} }
} }
# --- GENERATION MODE --- # --- GENERATION MODE ---
else { else {
# Default to SHA256 if no algorithm was specified in generation mode if ([string]::IsNullOrWhiteSpace($Algorithm)) {$Algorithm = 'SHA256'
if ([string]::IsNullOrWhiteSpace($Algorithm)) {
$Algorithm = 'SHA256'
} }
if (-not $FilePath -or $FilePath.Count -eq 0) { # Check if -o / -OutFile parameter was explicitly invoked
Write-Error "checksum: Missing file path parameter. Use -Help for usage." $wantOutFile =$PSBoundParameters.ContainsKey('OutFile')
return $outFilename =$OutFile
}
# Clear/initialize output file if specified before writing hashes if ($wantOutFile) {
if (-not [string]::IsNullOrWhiteSpace($OutFile)) { # Feature 2 & 3: Recursive, Wildcard + Passed output flag splits hashes strictly into localized directory files
$null = New-Item -Path $OutFile -ItemType File -Force if ($Recursive -and$isWildcardGlobal) {
} $groupedItems =$allItems | Group-Object DirectoryName
foreach ($pathEntry in $FilePath) { foreach ($group in$groupedItems) {
# Resolve wildcards and single file inputs $dirPath =$group.Name
$resolvedItems = Get-Item -Path $pathEntry -ErrorAction SilentlyContinue $filesInDir =$group.Group
if (-not $resolvedItems) { if ([string]::IsNullOrWhiteSpace($outFilename)) {
Write-Error "checksum: $pathEntry`: No such file or directory" $currentOutFile = Join-Path -Path$dirPath -ChildPath "checksums.$($Algorithm.ToLower())"
continue } else {
$currentOutFile = Join-Path -Path $dirPath -ChildPath$outFilename
}
# Clear existing file before iterating
$null = New-Item -Path$currentOutFile -ItemType File -Force
foreach ($item in $filesInDir) {$hash = (Get-FileHash -Path $item.FullName -Algorithm$Algorithm).Hash.ToLower()
$relativePath =$item.Name # Localization relies purely on internal filenames
$outputLine = "$hash$relativePath"
Add-Content -Path $currentOutFile -Value$outputLine
Write-Output $outputLine
}
}
} }
# Non-localized standard generation (Singular master file, or Per-file auto naming)
else {
$singleOutFile =$null
foreach ($item in $resolvedItems) { # Check for standard auto naming rules
# Handle directories like Linux sha256sum does if ([string]::IsNullOrWhiteSpace($outFilename)) {
if ($item.PSIsContainer) { if ($isWildcardGlobal) {$singleOutFile = "checksums.$($Algorithm.ToLower())"
Write-Error "checksum: $($item.Name)`: Is a directory" }
} else { } else {
$hash = (Get-FileHash -Path $item.FullName -Algorithm $Algorithm).Hash.ToLower() $singleOutFile =$outFilename
}
# Format standard relative path (stripping leading '.\') # Clear master file if consolidating to one file
$relativePath = Resolve-Path -Path $item.FullName -Relative if ($singleOutFile) {
$null = New-Item -Path$singleOutFile -ItemType File -Force
}
foreach ($item in $allItems) {$hash = (Get-FileHash -Path $item.FullName -Algorithm$Algorithm).Hash.ToLower()
$relativePath = Resolve-Path -Path$item.FullName -Relative
if ($relativePath.StartsWith(".\")) { if ($relativePath.StartsWith(".\")) {
$relativePath = $relativePath.Substring(2) $relativePath =$relativePath.Substring(2)
} }
$outputLine = "$hash $relativePath" $outputLine = "$hash$relativePath"
# If -OutFile is provided, write line to file if ($singleOutFile) {
if (-not [string]::IsNullOrWhiteSpace($OutFile)) { Add-Content -Path $singleOutFile -Value$outputLine
Add-Content -Path $OutFile -Value $outputLine } else {
# Single-file auto naming (<filename>.<algo>)
$currentOutFile = "$($item.FullName).$($Algorithm.ToLower())"
$null = New-Item -Path$currentOutFile -ItemType File -Force
Add-Content -Path $currentOutFile -Value$outputLine
} }
# Output to stdout
Write-Output $outputLine Write-Output $outputLine
} }
} }
} }
# Standard fallback if no -o was invoked: Stream perfectly to console
else {
foreach ($item in $allItems) {$hash = (Get-FileHash -Path $item.FullName -Algorithm$Algorithm).Hash.ToLower()
$relativePath = Resolve-Path -Path$item.FullName -Relative
if ($relativePath.StartsWith(".\")) {
$relativePath =$relativePath.Substring(2)
}
$outputLine = "$hash$relativePath"
Write-Output $outputLine
}
}
} }
} }
-85
View File
@@ -1,85 +0,0 @@
#!/bin/bash
# usage
## curl -s https://raw.githubusercontent.com/PrincessPi3/general-scripts-and-system-ssssssetup/refs/heads/main/customscripts/python_pyenv_setup.sh | sudo "$SHELL" && source ~/.zshrc && exec "$SHELL" && pyenv install 3:latest && pyenv global 3:latest
# set -e
# set rcfile
rcfile="$HOME/.zshrc" # zsh
# rcfile="$USER/.bashrc" # bash
package_log="/home/princesspi/python_packages_removed.txt"
# check if a command exists, and if so, purge that package
# usage: check_purge_package <package_name>
check_purge_package () {
package_name=$1
# which -s $package_name 2>/dev/null 1>/dev/null # silently test for command
# package_check=$? # get return code. 0 for found, any other for not found
# if [ $package_check -eq 0 ]; then # if package is found, purge it
echo "purging $package_name"
sudo apt purge $package_name -y 2>>"$package_log" 1>>"$package_log"
echo "finished purging $package_name"
# else # otherwise skip
# echo "no $package_name installation found, skipping uninstall"
#fi
}
# cleanup previous python installs
# check_purge_package pyenv*
# check_purge_package python3*
# check_purge_package python2*
# check_purge_package python*
# check_purge_package pip*
# check_purge_package pip3*
# check_purge_package pip2*
# cleanup
# echo "Autoremoving packages"
# sudo apt autoremove -y 2>>"$package_log" 1>>"$package_log"
# echo -e "DONE CLEARING OLD PACKAGES!\n\tRemoved packages logged to $package_log"
if [ -d $HOME/.pyenv ]; then
echo "Removing existing pyenv installation"
rm -rf $HOME/.pyenv
else
echo "No existing pyenv installation found, skipping removal"
fi
# install pyenv
curl -fsSL https://pyenv.run | bash
## setup shell
### check for pyenv in rcfile
grep -q .pyenv $rcfile
rc_pyenv_check=$? # get retcode, 0 for found not 0 for not found
if [ $rc_pyenv_check -eq 0 ]; then # if pyenv found
echo "pyenv already set up in $rcfile, skipping add init"
else # if pyenv not found
echo "Setting up pyenv in $rcfile"
echo -e "\n# pyenv setup" >> $rcfile
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> $rcfile
echo '[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"' >> $rcfile
echo 'eval "$(pyenv init - zsh)"' >> $rcfile
fi
## add aliases
### check for aliases in rcfile
grep -q '## python aliases' $rcfile
rc_pyalias_check=$? # get retcode, 0 for found not 0 for not found
if [ $rc_pyalias_check -eq 0 ]; then # if aliases found
echo "pyenv aliases already set up in $rcfile, skipping"
else # if aliases not found, add python shit
echo "Setting up pyenv aliases in $rcfile"
echo "## python aliases" >> $rcfile
echo 'alias py=python' >> $rcfile
echo 'alias py3=python' >> $rcfile
echo 'alias py2=python' >> $rcfile
echo 'alias python3=python' >> $rcfile
echo 'alias python2=python' >> $rcfile
echo 'alias pip3=pip' >> $rcfile
echo -e "alias pip2=pip\n" >> $rcfile
fi
echo -e "\n\ndone~ :3~\n\treset shell with exec \"\$SHELL\"\n"