Compare Files Byte by Byte with cmp, Then Trust the Exit Status
You will finish with a reliable way to check whether two files are identical, locate their first difference, compare only selected ranges, and use the result in a shell script. The examples use GNU cmp from diffutils 3.10, installed here as the diffutils package. Allow about ten minutes. You need two readable files and an ordinary shell; no command in this guide needs elevated privileges.
The route
Jump straight to the step you need, or tick off Done means at the end.
Checkpoint
This guide compares data. It does not alter either input, so there is no rollback step and no reason to run it with sudo.
1. Check the installed command
Confirm which executable your shell will run and record its version. This matters when you are moving a script between systems, because the options and diagnostics below are for GNU diffutils 3.10.
$ command -v cmp
/usr/bin/cmp
$ cmp --version | head -n 1
cmp (GNU diffutils) 3.10
If command -v finds another implementation, read that implementation's manual before copying these options into a script. The command compares raw bytes, not text lines or characters, so a changed newline, encoding, or metadata byte can make two apparently similar files different.
2. Test for equality without producing noise
Pass two file names. With identical inputs, cmp prints nothing and returns status 0. With different inputs, it reports the first differing byte and line, then returns status 1.
$ cmp /path/to/original.bin /path/to/copy.bin
$ printf 'cmp status: %s\n' "$?"
cmp status: 0
$ cmp /path/to/original.bin /path/to/suspect.bin
/path/to/original.bin /path/to/suspect.bin differ: byte 4097, line 1
$ printf 'cmp status: %s\n' "$?"
cmp status: 1
Do not interpret silence as a failure. Silence plus status 0 is the successful equality case. The byte and line numbers start at 1. A binary file can report line 1 for a difference even when it has no useful line structure.
Checkpoint
Repeat the command with a known identical pair. You should see no comparison output and an exit status of 0.
3. Read a first-difference diagnostic
For a human investigation, add --print-bytes to show the two differing byte values, or --verbose to list every differing byte. These modes still return 1 when differences are found.
$ cmp --print-bytes /tmp/left.bin /tmp/right.bin
/tmp/left.bin /tmp/right.bin differ: byte 4, line 1 is 130 X 170 x
$ cmp --verbose /tmp/left.bin /tmp/right.bin
4 130 170
The verbose output is a stream of byte number, value from the first file, and value from the second file. Values are decimal. Use it when you need a complete list, but avoid sending a large output stream straight to a terminal if the files may differ extensively. Redirect it to a review file only when that file is safe to create and has a deliberate name.
cmp is not a patch generator and does not explain a text change in words. If you need line-oriented additions and removals, use a suitable diff tool after this byte-level check.
4. Compare only a prefix or skip file headers
Use --bytes=LIMIT when a prefix is the only part that matters. If the first LIMIT bytes match, the command returns 0 even if the files differ later. That is useful for a format signature or protocol header, but it is not a whole-file verification.
$ cmp --bytes=16 /path/to/header-a.bin /path/to/header-b.bin
$ printf 'header status: %s\n' "$?"
header status: 0
Use --ignore-initial=SKIP to skip the same number of bytes in both inputs. To skip different header lengths, use the GNU form --ignore-initial=SKIP1:SKIP2. The equivalent positional operands are accepted after the file names: cmp FILE1 FILE2 SKIP1 SKIP2.
$ cmp --ignore-initial=512:1024 /path/to/left.img /path/to/right.img
$ printf 'payload status: %s\n' "$?"
payload status: 0
Check the format specification before choosing offsets. Skipping the wrong number of bytes can make unrelated data appear equal, which is a verification error rather than a harmless convenience. Size suffixes are available: K means 1024, while kB means 1000; M and MB follow the same distinction.
5. Use standard input deliberately
A file name of - means standard input. This lets you compare a file with a generated stream without first saving that stream, but remember that a pipe is consumed once.
$ cmp /path/to/reference.txt - < /path/to/generated.txt
$ printf 'stream status: %s\n' "$?"
stream status: 0
Do not put two independent commands in a pipeline and assume cmp's status is preserved by every shell. If a script needs the comparison result, capture it immediately as shown, or use the shell's pipeline-status facility explicitly. Keep untrusted file names quoted, especially when they begin with a hyphen; use a path such as ./-input.bin when necessary.
6. Make scripts distinguish different from broken
There are three relevant statuses:
0: the selected inputs matched.1: the selected inputs differed.2:cmpcould not complete, such as when a file is missing or unreadable.
A compact shell check can handle the expected difference without hiding an operational error:
if cmp --quiet -- /path/to/expected.bin /path/to/actual.bin; then
printf '%s\n' 'match'
else
status=$?
case "$status" in
1) printf '%s\n' 'different' ;;
2) printf 'comparison failed (status %s)\n' "$status" >&2; exit "$status" ;;
*) printf 'unexpected cmp status %s\n' "$status" >&2; exit "$status" ;;
esac
fi
--quiet suppresses normal comparison output, while -- marks the end of options. Test the error path with a deliberately wrong path if you are checking a script, then restore the real path. Never turn status 1 into a generic command failure without deciding whether 'different' is an expected result for your workflow.
Done means
- You checked that the installed
cmpis GNU diffutils 3.10, or adjusted the examples for your implementation. - You know that status 0 means a match, status 1 means a difference, and status 2 means trouble.
- You used
--bytesor--ignore-initialonly with offsets justified by the file format. - You quoted paths, treated standard input as a one-use stream, and kept comparison commands unprivileged.