Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

md5sum — Compute and verify MD5 message digests

Overview

md5sum prints a 128-bit MD5 digest of each input file (or of standard input), and — in check mode — re-verifies files against a previously saved digest list. It is the anchor of the coreutils checksum family: sha1sum, sha256sum, sha512sum, b2sum and friends all copy its output format, its -c verification protocol, and its exit-code conventions almost verbatim. Learn the format once here and every sibling tool reads the same way.

On Debian and Ubuntu the binary ships in the coreutils package and lives at /usr/bin/md5sum. It is not a POSIX utility: the Single UNIX Specification has no checksum tool at all. It is nonetheless present on essentially every Linux system (busybox provides a compatible subset), on macOS via md5 (different name, same algorithm), and on Windows through various ports. GNU md5sum has been around since the textutils days and joined GNU coreutils when textutils, fileutils and sh-utils merged in the early 2000s.

Reach for md5sum when you need a quick, human-verifiable integrity fingerprint: downloads copied from a vendor’s checksum list, files replicated between machines, tarballs in a build pipeline. Reach for sha256sum instead whenever an adversary could craft input deliberately — MD5 is cryptographically broken in the collision sense (see Nuances), and has been for nearly two decades.

It is often confused with md5 (BSD name), with openssl dgst -md5 (same digest, different output format), and with cksum/sum (CRC-style, non-cryptographic, different purpose entirely).

FieldValue
Packagecoreutils (Debian bookworm)
Section (man)1 — user commands
Path/usr/bin/md5sum
First appeared / lineageMD5 algorithm (RFC 1321, 1991); tool from GNU textutils, merged into GNU coreutils
StandardsNot POSIX; output format is the de facto template for the whole coreutils checksum family

Synopsis

md5sum [OPTION]... [FILE]...
md5sum file.iso                  # print digest of file.iso
md5sum -c sums.md5               # verify files listed in sums.md5
md5sum --tag file.iso            # BSD-style tagged output
md5sum -z -c sums0.md5           # verify NUL-delimited checksum list

With no FILE, or when FILE is -, standard input is read. The two-space separator between digest and name, and the - placeholder for stdin, are fixed by convention and parsed back by -c.

How It Works

The MD5 algorithm, briefly

MD5 processes input in 512-bit blocks. Each block is mixed through four rounds of a compression function against a 128-bit chaining state; after the final block (with mandatory Merkle–Damgård padding: a 1 bit, zeros, then the 64-bit message length), the chaining state is serialized little-endian into 16 bytes and printed as 32 hex characters. The construction is deterministic: the same bytes always yield the same digest, on every machine and every endian.

The padding means the digest depends on the exact byte stream — trailing whitespace, CRLF translation, or a missing final newline all change the result. md5sum is a byte counter wearing a cryptographer’s hat.

Output line format

This is the format every sibling tool inherits. Memorize it:

┌───────────────────── digest: 128 bits = 32 hex chars
│      ┌───────────── space (text mode) or '*' (binary mode)
│      │ ┌─────────── filename, or '-' for stdin
▼      ▼ ▼
d41d8cd98f00b204e9800998ecf8427e  empty.txt
764efa883dda1e11db47671c4a3bbd9e  -

The mode marker is a historical artifact. On DOS/Windows ports the byte stream differs between text and binary I/O, so the two modes can genuinely diverge; on GNU systems both modes read identical bytes, and the field exists only so -c can parse files produced anywhere. GNU md5sum defaults to text mode (<space>) and prints * when given -b.

Tagged output and cross-format parsing

--tag switches to the BSD-style format used by OpenSSL and macOS md5:

$ md5sum --tag /tmp/f1.txt
MD5 (/tmp/f1.txt) = ba2f0f3909a4fb539a5dd22e7b0075e8

Check mode (-c) accepts both formats on input — plain two-space lines, * binary lines, and tagged MD5 (name) = digest lines — so a manifest generated by BSD tools or OpenSSL can be verified directly.

Filenames containing backslashes or newlines

A checksum line is newline-delimited, so a filename with an embedded newline would be unparseable. GNU tools solve this with an escape marker: if the filename contains a backslash or a newline, the whole line is prefixed with \ and the name is escaped (\n becomes the two characters backslash-n, a literal backslash becomes two backslashes):

$ printf 'x' > $'we\nird.txt'
$ md5sum $'we\nird.txt' | od -c | head -4
0000000   \   9   d   d   4   e   4   6   1   2   6   8   c   8   0   3
0000020   4   f   5   c   8   5   6   4   e   1   5   5   c   6   7   a
0000040   6           w   e   \   n   i   r   d   .   t   x   t  \n

Note the leading \ before the digest and the w e \ n i r d . t x t spelling. In check mode the same rule is reversed. The bulletproof alternative for weird names is -z: output (and expect) NUL-terminated records instead of newline-terminated ones, pairing cleanly with find -print0 and xargs -0.

Check mode

With -c/--check, md5sum reads checksum lines, recomputes each digest, and reports per file. The decision flow:

read line ──► parse OK? ──no──► "improperly formatted" warning
                 │                (--strict: exit nonzero; else skip line)
                 yes
                 ▼
        file exists/readable? ──no──► "FAILED open or read"   [exit 1]
                 │
                 yes
                 ▼
        recomputed == stored? ──no──► "file: FAILED"          [exit 1]
                 │
                 yes
                 ▼
        "file: OK" (suppressed by --quiet)                    [exit 0]

End-to-end demonstration with real output:

$ printf 'hello\n' > f1.txt
$ md5sum f1.txt > f1.md5
$ cat f1.md5
b1946ac92492d2347c6235b4d2611184  f1.txt
$ md5sum -c f1.md5
f1.txt: OK
$ printf 'tampered\n' > f1.txt
$ md5sum -c f1.md5
f1.txt: FAILED
md5sum: WARNING: 1 computed checksum did NOT match

A summary WARNING: line always goes to stderr when something failed, even under --status; --status silences stdout entirely and reduces the whole run to the exit code, which is what scripts and cron jobs want. --quiet keeps per-file FAILED lines but stops printing OK for passing files — handy for large manifests.

Streaming and memory behavior

md5sum reads input in fixed-size chunks and never buffers whole files in memory; a 100 GB log and a 100 B note cost the same RAM. Digesting a FIFO or /dev/stdin works fine. What is not possible is resuming: digests are all-or-nothing over the byte stream, so verification always re-reads the full file. On modern CPUs MD5 runs at several GB/s since it is not deliberately hardened against acceleration; it is rarely the bottleneck next to disk I/O.

Options That Matter

Output control

OptionEffect
-b, --binaryPrefix filename with * marker (meaningful only on text/binary-divergent platforms)
-t, --textPrefix with space marker; the default on GNU
--tagEmit BSD-style MD5 (file) = digest lines instead of the default format

Check mode

OptionEffect
-c, --checkRead digest lines from FILE (or stdin) and verify each
--quietDon’t print OK per passing file; failures still reported
--statusPrint nothing; exit code is the only signal
--strictTreat improperly formatted input lines as an error (nonzero exit)
-w, --warnWarn about improperly formatted lines, but keep exit 0
--ignore-missingSkip entries whose files don’t exist (partial verification of partial checkouts)

Input handling

OptionEffect
-z, --zeroNUL-terminate records on output; in check mode, expect NUL-delimited input
--tag / plain outputBoth formats accepted by -c regardless of which produced them

Usage Patterns

# Fingerprint a downloaded ISO against the vendor's published list
md5sum debian-12.5.0-amd64-netinst.iso
# Verify a downloaded manifest in one shot
wget -qO- https://example.com/release/SHA256SUMS-STYLE.md5 | md5sum -c -
# Manifest for a whole tree, relative paths so it verifies after the tree moves
cd /srv/data && find . -type f -print0 | sort -z | xargs -0 md5sum > MANIFEST.md5
# Later: verify the tree (run from the same directory)
cd /srv/data && md5sum -c MANIFEST.md5 --quiet || echo 'drift detected'
# Digest of a string without touching the filesystem
printf '%s' 'exact bytes, no newline' | md5sum
# Digest of a string WITH the newline echo would add — note the different hash
echo 'exact bytes, no newline' | md5sum
# Digest every file in a directory listing, NUL-safe end to end
find . -maxdepth 1 -type f -print0 | xargs -0 md5sum -b
# Script-friendly check: silence stdout, act on the exit code
if md5sum -c --status backup.md5; then echo 'backup intact'; else alert; fi
# Spot which files changed between two snapshots
md5sum -c old-manifest.md5 2>/dev/null | grep -v ': OK$'
# Verify only the files you actually kept from a release archive
md5sum -c --ignore-missing SHA256SUMS 2>&1 | grep -v 'no such'
# Tagged format for interop with OpenSSL-style tooling
md5sum --tag bundle.tar.gz
# Deduplicate by content: group files whose digests match
md5sum *.jpg | sort | awk 'seen[$1]++ {print $2}'

Nuances and Gotchas

  • MD5 is broken for adversarial input. Practical collision constructions have existed since 2004 (Wang et al.), rogue-CA certificate forgery followed in 2008, and the Flame malware forged a Microsoft code-signing signature with an MD5 collision in 2012. Two chosen inputs with identical digests can be built cheaply. MD5 remains acceptable for detecting accidental corruption (bit rot, truncation, bad copy) and is still common for that, but any security-relevant use — package signatures, file-integrity monitoring against attackers, password hashing — must move to SHA-256 or better. See ./sha256sum.md.
  • A collision is not a preimage. MD5 collisions are easy; finding input that matches a given digest is still infeasible. That asymmetry is why md5sum -c against a vendor manifest still catches real corruption even today — but it does not make MD5 safe where an attacker controls the files.
  • Trailing bytes matter. echo hi | md5sum and printf 'hi' | md5sum differ because echo appends a newline. When comparing a digest you computed against a published one, reproduce the exact byte stream, including the newline you may not have known was there.
  • Relative paths in manifests are position-sensitive. md5sum -c resolves filenames as written, from the current directory. A manifest made with absolute paths only verifies at the same location; with relative paths it verifies anywhere the tree has the same layout. Build manifests with cd into the tree first.
  • Filenames with newlines silently corrupt plain manifests. Any pipeline that feeds md5sum with unquoted $(...) or line-oriented processing breaks on such names. Use -print0/xargs -0/-z or accept the escape-marker format.
  • -c exit code granularity. Any single failure — mismatch, missing, unreadable — is exit 1; malformed input lines are ignored unless --strict is given. Scripts that must treat parse errors as fatal need --strict.
  • busybox is a subset. busybox md5sum supports -c and -b but not --tag, --status, --strict or --ignore-missing. Scripts for embedded images should stick to the POSIX-ish core options.
  • md5sum is not the same tool as md5 (BSD/macOS) or openssl dgst -md5: identical algorithm, different output syntax. -c understands some of these formats, but generating and checking with the same tool avoids surprises.
  • stdin digests use the - name. A manifest line for stdin reads <digest> - and checking it would try to hash a file literally named -; keep manifests to real paths.

Exit Status

CodeMeaning
0All requested work succeeded: digest printed, or every checked file passed OK
1At least one checked file failed verification (FAILED), could not be opened (FAILED open or read), or — with --strict — an improperly formatted checksum line was seen; also covers open/read errors in hash mode

With --status the same codes apply; nothing is printed. Without --strict, malformed lines only produce a stderr warning and are skipped, so they do not affect the exit code as long as the remaining files verify.

  • sha256sum — the default replacement for anything security-relevant; same format and flags.
  • sha1sum — next-weakest sibling, also collision-broken; kept for legacy compatibility.
  • sha512sum — 64-bit-word sibling, often faster than SHA-256 on 64-bit CPUs.
  • b2sum — BLAKE2: modern, fast, variable-length digests, keyed mode via -l and external keying.
  • sum — legacy BSD/SysV 16-bit checksums: cheap error detection, no cryptographic value.
  • wc — the other “stream it and print a number” tool; pairs with md5sum in one-pass inventory pipelines.
  • overview — collection hub for the GNU Coreutils binaries.

Interview Questions

Q: What exactly is stored on a line of an md5sum manifest, and what can vary?

Three fields: the 32-hex-character digest, a two-character separator whose second character is space for text mode or * for binary mode, and the filename (- for stdin). GNU md5sum accepts all variants when checking, and additionally accepts BSD-tagged lines like MD5 (name) = digest. The mode character matters only on platforms where text and binary I/O produce different byte streams — on Linux it is pure compatibility syntax.

Q: A colleague says “MD5 checksums guarantee the ISO I downloaded wasn’t tampered with.” Where’s the flaw?

The manifest itself must come from a channel an attacker can’t modify, and MD5 must actually resist that attacker. Collisions in MD5 are cheap to construct, so an adversary who controls both the file and the published digest can craft two ISOs with identical MD5 (one benign, one malicious) and publish the shared digest. Integrity of downloads therefore needs the digest transported over TLS or signed (GPG), and computed with a collision-resistant function such as SHA-256. MD5’s remaining honest use is detecting accidental corruption.

Q: md5sum -c returns 1 but prints f1.txt: OK for every file you see. What do you investigate?

The stderr stream, not stdout. The usual causes: a WARNING: N listed file could not be read (missing file — check with --ignore-missing to see only the digest comparisons), a no properly formatted checksum lines found message (wrong file, or the manifest was CRLF-translated or truncated), or improperly formatted lines that --strict elevated to an error. Run the same command with stdout discarded (md5sum -c manifest 2>&1 >/dev/null) and the real complaint appears.

Q: Why do checksum tools use Merkle–Damgård padding, and what’s the practical consequence for md5sum?

The message must be a whole number of 512-bit blocks, so MD5 appends a 1 bit, zero bits, and the 64-bit message length. The practical consequence: two different byte streams that differ only by trailing padding bytes produce different digests — hi and hi\n hash differently — so any comparison against a published digest must be done over the exact same bytes, not the “same” content. It also means the digest length doesn’t leak the exact message length only because the length is folded into the final block.

Q: How would you verify 2 million files nightly without the report drowning in OK lines?

Generate the manifest once with find -print0 | sort -z | xargs -0 md5sum -z (NUL-safe, sorted so diffs are meaningful). Nightly, run md5sum -z -c --quiet manifest from the tree root: --quiet suppresses OK lines so output contains only failures, and --status reduces everything to an exit code if the consumer is a script. Wrap it with --strict so a corrupted manifest itself alarms instead of silently verifying nothing.

Q: Is md5sum still worth teaching, or is it pure legacy?

Purely as a hash it is legacy, but the tool matters for three reasons: the .md5 manifest format is the template every coreutils checksum tool uses (one mental model, five tools); enormous operational tooling still ships MD5 expectations and you must know how to check and migrate them; and the collision/preimage distinction — why MD5 is dead for signatures yet usable for corruption checks — is a standard interview discriminator between memorized and understood answers.

References