linux · difficulty ◆◆
diff — compare files line by line
Two files, one truth: the exact lines that differ.
Every git diff you have ever read is this command wearing a costume. Learn the original and the unified format stops being a mystery of line numbers and becomes a sentence you can read aloud.
AI-generated draft · reviewed and editedThis text was generated with AI as a first draft, then reviewed and edited by the author, who holds editorial responsibility for it.$ diffWhat it does
diff compares two files (or entire directory trees with -r) line by line and prints just the differences. Two files that match produce no output. When they differ, diff shows the offending lines: a line added on one side, removed on the other, or changed — each prefixed with < for the first file and > for the second. Its exit status is a truth machine: 0 means identical, 1 means different, 2 means trouble. That exit code is what makes diff usable inside shell scripts and is the whole reason git, patch, and every CI pipeline lean on it.
Why it matters
diff is the answer to the oldest sysadmin question: 'what changed, and where?' You point it at two nginx configs, two versions of a deploy script, two directories before and after a release, and diff tells you the exact lines. Its sibling patch can then apply those differences to other copies of the original for you. Master diff's exit code and the -r and -q flags and you stop eyeballing files side by side — the machine does the scanning.
Example
$ diff nginx-old.conf nginx-new.conf4a5
> server_tokens off;
Normal diff. '4a5' reads 'after line 4 of file 1, add the line that is line 5 of file 2'. < means from the first file, > from the second. Exit code is 1 because the files differ.
$ diff -r /tmp/d1 /tmp/d2Only in /tmp/d1: extra.txt
Only in /tmp/d2: other.txt
diff -r /tmp/d1/sub/config /tmp/d2/sub/config
2c2
< line2
---
> line2 changed
-r recurses into directories, reporting files present in only one tree and recursing into the ones that exist on both. This is the go-to check before and after a deploy.
$ diff -q nginx-old.conf nginx-new.confFiles nginx-old.conf and nginx-new.conf differ
-q (brief) prints only a yes/no, making it perfect for shell scripts: if diff -q a.txt b.txt; then echo same; else echo diff; fi — no noise to parse.
$ diff -y nginx-old.conf nginx-new.confserver { server {
listen 80; listen 80;
server_name example.com server_name example.com;
root /var/www/html root /var/www/html;
> server_tokens off;
} }
-y prints both files side by side with a > marker where they diverge. Great for a quick eyeball on short configs.
Common flags
- -u
- unified format with context lines — the git-diff style; best for humans and patch
- -q / --brief
- report only whether files differ, used in scripts to test equality
- -s
- say when files are IDENTICAL (default is silence on a match)
- -r
- recursively compare matching files inside two directories
- -y
- side-by-side output in two columns instead of a change list
- -N
- treat files missing from one side as empty, so additions show as content not 'only-in'
- -x PATTERN
- exclude files matching a pattern when recursing (e.g. -x '*.log')
History
Origin
diff was written by Douglas McIlroy at Bell Labs and first shipped in the fifth edition of Unix (1974). McIlroy headed the lab's computing research and had a knack for small tools that composed — diff was the template for the whole 'compare two things' genre, and it spawned patch, sdiff, and the format that still powers version control today.
The unified revolution
The default diff format (the 4a5 / 2c2 you see by default) is terse and hard to read. In 1990 Wayne Davison designed the unified format — the -u you now reach for reflexively — which folds removals and additions into one run of context lines. It was so obviously better that it became the de facto standard, and it is exactly what git diff, GitHub, and every code-review tool print today.
Fun facts
Pros & cons
pros
- + Universal: present on every Unix-like system since 1974, no install
- + Exit status 0/1/2 makes it scriptable — it doubles as an equality test
- + Handles whole directory trees with -r, plus exclusions and side-by-side views
cons
- − Default format is terse and hard to read; you must remember -u
- − Line-oriented only — moving a single line within a big file registers as delete + add
- − No smart move/rename detection like git diff; it compares text, not intent
Takeaways
- 1Default to diff -u: context lines make changes readable and patch-friendly
- 2Use diff -q in scripts as a quiet equality test — check the exit code, not output
- 3Diff whole trees before/after a deploy with diff -r dir1 dir2
- 4Reach for diff -s to explicitly confirm two files are identical
- 5Read @@ -a,b +c,d @@ as old-file range a–b maps to new-file range c–d
