Skip to content
shellmap

Hash a file

Compute a cryptographic checksum (SHA-256 by default, MD5 / SHA-1 for legacy interop) of a file's bytes — for verifying downloads, deduplicating, comparing against a published manifest, or generating cache-bust keys.

How to hash a file in each shell

Bashunix
sha256sum file.bin

GNU coreutils ships `sha256sum` / `sha1sum` / `md5sum` — each prints `HASH FILENAME` (two spaces, the trailing column is the filename, NOT the hash). For just the hash: `sha256sum file | cut -d" " -f1`. To verify a manifest: `sha256sum -c CHECKSUMS.txt` checks every line. Cross-OS-portable: `openssl dgst -sha256 file` works on every Unix with openssl (output format differs: `SHA256(file)= HASH`). For data from a pipe: `cat file | sha256sum` — but be aware this is slower than `sha256sum file` because `cat` adds a copy.

Zshunix
shasum -a 256 file.bin

**macOS BSD lacks `sha256sum`** — the BSD-userland equivalent is `shasum -a 256` (Perl script shipping with macOS, supports `-a 1|224|256|384|512`). `md5` (no `sum`) is the BSD MD5 binary — different output format than GNU `md5sum` (BSD: `MD5 (file) = HASH`; GNU: `HASH file`). For full cross-OS portability in scripts that may run on both: prefer `openssl dgst -sha256 file` (uniform output `SHA256(file)= HASH` on both). `brew install coreutils` adds `gsha256sum` etc. with the GNU output format.

Fishunix
sha256sum file.bin

Same externals (`sha256sum` on Linux, `shasum -a 256` on macOS). Fish-native variable capture: `set hash (sha256sum file.bin | string split " ")[1]` — the `string split` produces a list, `[1]` picks the first element (the hash). For verifying a download: `set expected abc123…; set actual (sha256sum download.bin | string split " ")[1]; test "$expected" = "$actual"; and echo OK; or echo MISMATCH`.

PowerShellwindows
Get-FileHash file.bin

`Get-FileHash` (pwsh 4.0+ / Windows 8.1+) defaults to **SHA256** — different from many Unix tools where SHA-256 needs to be explicitly requested. Override: `Get-FileHash file -Algorithm MD5|SHA1|SHA384|SHA512`. Output is an object with `Algorithm`, `Hash`, `Path` properties; the hash is UPPERCASE hex by default (different from GNU lowercase). For just the hash, lowercase, matching `sha256sum` format: `(Get-FileHash file).Hash.ToLower()`. For a stream / string instead of a file: there's no built-in for streams — use `.NET` directly: `[BitConverter]::ToString([Security.Cryptography.SHA256]::Create().ComputeHash([Text.Encoding]::UTF8.GetBytes("hello"))).Replace("-","").ToLower()`.

cmd.exewindows
certutil -hashfile file.bin SHA256

`certutil -hashfile FILE [ALGORITHM]` is the cmd-native answer — algorithms: `MD5`, `SHA1`, `SHA256`, `SHA384`, `SHA512`. Output is verbose (three lines: a header, the hash, a footer) — extract just the hash with `findstr` or shell out to pwsh. The hash line is the only one that's pure hex: `for /f "skip=1 delims=" %i in ('certutil -hashfile file.bin SHA256') do @echo %i & exit /b`. Quirks: uppercase output, spaces between bytes on older Windows (`A1 B2 C3 …`) vs continuous string on Windows 10+. For predictable cmd hashing, shell out to pwsh: `powershell -NoProfile -Command "(Get-FileHash file.bin).Hash"`.

Equivalents listed for Bash, Zsh, Fish, PowerShell, cmd.exe.

Gotchas & notes

  • **The trailing-newline trap**: `echo "hello" | sha256sum` and `printf hello | sha256sum` produce DIFFERENT hashes — `echo` appends a newline (`hello\n`, hash starts `5891b5b…`); `printf` does not (`hello`, hash starts `2cf24db…`). When verifying a hash, ALWAYS know whether the source data has a trailing newline. Same trap for `Get-FileHash` of a file written by `Set-Content` (which adds CRLF on Windows pwsh 5.1, LF on pwsh 6+) — the file bytes differ across pwsh versions, so the hash differs. For canonical text hashing, normalize line endings first (`tr -d "\r"` strip CRLF, `dos2unix`, or set encoding explicitly when writing).
  • **Algorithm choice**: SHA-256 is the modern default for new code — universally available, fast on every modern CPU (often hardware-accelerated via Intel SHA-NI / ARM crypto extensions). MD5 is BROKEN cryptographically (collision attacks demonstrated in 2008) — fine for non-security checksums (cache keys, content-addressable storage), NOT for security (don't use for password hashing, signature verification, content authenticity). SHA-1 is also broken (Google SHAttered, 2017) — same rule. SHA-3 / BLAKE3 are newer alternatives; BLAKE3 is much faster (parallelizable) and SHA-256-strength security but less ubiquitously installed.
  • **Output format normalization**: GNU `sha256sum` → `HASH FILENAME` (lowercase). BSD `shasum -a 256` → `HASH FILENAME` (lowercase, Perl, same format despite different binary). `openssl dgst -sha256` → `SHA256(FILENAME)= HASH` (lowercase, parentheses). pwsh `Get-FileHash` → object with UPPERCASE `Hash` property. cmd `certutil -hashfile` → multi-line verbose with UPPERCASE hash. When scripting across all five: prefer `openssl` on Unix + `Get-FileHash` on Windows, both lowercased for comparison: `[ "$(openssl dgst -sha256 file | cut -d" " -f2)" = "$EXPECTED" ]` / `(Get-FileHash file).Hash.ToLower() -eq $expected`.
  • **Verifying against published manifests**: many open-source releases publish `SHA256SUMS` (Debian / Ubuntu / Linux kernel convention). Verify with `sha256sum -c SHA256SUMS` (GNU) or `shasum -a 256 -c SHA256SUMS` (BSD). The file format is `HASH FILENAME` per line — both tools parse it identically. For SIGNED manifests (e.g. `SHA256SUMS.gpg`), verify the signature first (`gpg --verify SHA256SUMS.gpg SHA256SUMS`), THEN verify the hashes — order matters because an attacker who can swap the manifest can fake any hash.

Related commands

Related tasks