Linux & Bash · Part I · Module 4 · Reading and Inspecting File Contents
Comparing Two Files: diff and cmp
Chapter 4.5 could tell you that two files have different numbers of lines. This chapter tells you which lines, where, and what changed about them. That is useful on its own, and the format it answers in is worth more than the command: the same notation appears in every code review you will ever take part in, every patch anyone sends you, and every version-control tool you will use. Learning to read it here, on two small files you control, is considerably easier than learning to read it later under pressure on somebody else's change.
Foundation16 min readLinuxPart IFundamentalsFilesComparison
Part I · Module 4 · Chapter 4.6 · Comparing Two Files
1. The Question
Two files that should be the same, and are not.
$ wc -l expected.txt actual.txt
4 expected.txt
5 actual.txt
9 totalChapter 4.5 got you this far: they differ, by about one line. What it cannot tell you is which line, or whether that one extra line is the only change.
$ diff expected.txt actual.txt
3c3
< charlie
---
> CHARLIE
4a5
> extraTwo changes, precisely located. Line 3 was changed, and a line was added after line 4.
2. Reading the Default Format
The default output is terse and worth being able to read, though §3's format is the one you will meet more often.
3c3 line 3 of the first file was CHANGED into line 3 of the second
< charlie the first file's version (< points at the first file)
--- separator
> CHARLIE the second file's version (> points at the second file)
4a5 after line 4 of the first, line 5 of the second was ADDED
> extra the added lineThe letters are the grammar:
| Letter | Means |
|---|---|
c | changed |
a | added |
d | deleted |
And the direction markers:
| Marker | Which file |
|---|---|
< | the first file named on the command line |
> | the second file named |
That last point matters more than it looks. diff has no idea which of your files is the "right" one — it reports the first as < and the second as > and leaves the judgement to you. Swap the order on the command line and every < becomes a >. The usual convention is old first, new second, so that the output reads as "what changed to get here".
3. The Unified Format
$ diff -u expected.txt actual.txt
--- expected.txt 2026-03-22 11:42:32
+++ actual.txt 2026-03-22 11:42:32
@@ -1,4 +1,5 @@
alpha
bravo
-charlie
+CHARLIE
delta
+extraThis is the one to learn. It is called unified because it shows both versions interleaved in one block with context around them, instead of separating them as the default format does.
Decoded:
--- expected.txt the FIRST file. Its lines are marked with -
+++ actual.txt the SECOND file. Its lines are marked with +
@@ -1,4 +1,5 @@ location: first file lines 1-4, second file lines 1-5
alpha a leading SPACE means unchanged — context
bravo context
-charlie only in the first file (removed)
+CHARLIE only in the second file (added)
delta context
+extra only in the second file (added)Four kinds of line, and the first character of each is what tells you which:
| First character | Meaning |
|---|---|
| space | unchanged — shown for context |
- | in the first file only |
+ | in the second file only |
@@ | a location header |
A changed line appears as a - immediately followed by a +: removed in one version, added in the other. There is no separate "changed" marker, because a change is a removal plus an addition.
Reading the @@ header
@@ -1,4 +1,5 @@Two pairs, matching the two files:
-1,4— in the first file, this block covers 4 lines starting at line 1;+1,5— in the second file, it covers 5 lines starting at line 1.
A long file produces several of these blocks, each called a hunk, with a header saying where it sits. Skipping from one @@ to the next is how you move through a large diff.
4. The Exit Status Carries the Answer
Something new happens here, and it is worth stopping on.
$ diff expected.txt expected.txt
$ echo $?
0
$ diff expected.txt actual.txt
3c3
< charlie
---
> CHARLIE
$ echo $?
1
$ diff expected.txt nope.txt
diff: nope.txt: No such file or directory
$ echo $?
2Three different statuses, each meaning something specific:
| Status | Means |
|---|---|
0 | the files are identical |
1 | the files differ |
2 | something went wrong — a file is missing or unreadable |
Every command so far has used the status only for "worked" or "did not work". diff uses it to carry an answer, and it is the first command in this course to do so.
Notice too that identical files produce no output at all:
$ diff expected.txt expected.txt
$Silence means "no differences", which is Chapter 1.2's convention applied to a question rather than an action. A diff that prints nothing is the good outcome.
This is what makes diff usable by something other than a person. A later command can ask "are these two files the same?" and act on the answer without reading any output. Module 11 teaches exit status properly and Module 16 uses it in conditions; the thing to carry now is that the number is deliberate and means three different things.
5. Options Worth Knowing
Just tell me whether they differ.
$ diff -q expected.txt actual.txt
Files expected.txt and actual.txt differOne line instead of a full listing. Useful when you have many files and only want to know which ones need attention.
Ignore things you do not care about.
$ diff -w old.txt new.txt # ignore all whitespace differences
$ diff -i old.txt new.txt # ignore case
$ diff -B old.txt new.txt # ignore blank lines-w is the one you will reach for most. Reformatted indentation can make two files look completely different when nothing meaningful changed, and -w cuts through that.
Use them with care, though: whitespace sometimes is the difference that matters, and Chapter 4.7's line-ending problem is exactly a whitespace difference that -w would hide.
Compare whole directories.
$ diff -r dir_a dir_b-r walks both trees and compares matching files, also reporting files that exist in one and not the other. Useful for checking that a copy came out right — a direct follow-on from Chapter 3.2.
Side by side, when the unified format is hard to read:
$ diff -y old.txt new.txtTwo columns with markers between them. Easier for small files on a wide terminal, unwieldy otherwise.
6. cmp — For Files That Are Not Text
diff works line by line, which assumes lines. For a file that is not text, that assumption does not hold.
$ cmp image_a.png image_b.png
image_a.png image_b.png differ: char 13, line 3cmp compares byte by byte and stops at the first difference, reporting where it is. It makes no attempt to describe the change, because for a binary file there is nothing useful to say.
Identical files produce silence, like diff:
$ cmp image_a.png image_a.png
$ echo $?
0And the quiet form, for when you only want the answer:
$ cmp -s a.bin b.bin
$ echo $?
1-s prints nothing at all and reports only through the exit status. This is the standard way to ask "are these two files byte-for-byte identical?" and is the right question for a copy you want to verify.
| Use | Command |
|---|---|
| how do these two text files differ | diff -u |
| are these two files identical at all | cmp -s |
| where does the difference start, in a binary | cmp |
A further note: diff will tell you that two binary files differ, but not how — it has nothing sensible to show. That is cmp's job, and for actually reading a binary difference you need tools beyond Part I.
7. The Failures
Reading the direction backwards. < and - are the first file; > and + are the second. Getting this wrong inverts your entire understanding of a change. When in doubt, look at the --- and +++ header lines, which name the files explicitly.
Expecting output when the files match. Silence is the success case. A diff that prints nothing has answered your question.
Comparing files with different line endings. Two files that look identical can diff as completely different, with every single line marked changed. That is Chapter 4.7's subject and the single most confusing diff result there is — the next chapter diagnoses it in one command.
Using -w to make a confusing diff go away. It hides whitespace differences, and sometimes the whitespace is the bug. Use it when you know the reformatting is irrelevant, not to quieten something you have not understood.
Comparing a file with a directory. diff file dir compares the file against a same-named file inside the directory, which is occasionally what you wanted and often a surprise.
8. Why This Matters in Engineering Work
Comparison is how you answer "what changed?", and that question is most of debugging.
A run that used to work. Two logs, one good and one bad:
$ diff -u good_run.log bad_run.log | head -n 40The first real difference is usually at or just before the cause. Piping into head keeps the output manageable when the logs diverge badly — the pipe from Chapter 4.3, again.
Comparing the conditions rather than the output. Chapter 4.3 made the point that the top of a log records versions and settings. Two runs that behaved differently often differ in those first twenty lines rather than anywhere interesting:
$ diff <(head -n 20 good.log) <(head -n 20 bad.log)That form takes two command outputs and compares them directly. The syntax belongs to Module 12; it is shown because it is the natural shape of the question and you will see it in other people's work.
Verifying a copy. cmp -s answers whether a transferred file arrived intact, which matters when the copy crossed a network — Chapter 3.2's point about moving between filesystems.
Checking output against a known-good reference is the backbone of automated testing, and it is this command plus its exit status. Part II builds exactly that.
9. Practice
Use expected.txt containing alpha, bravo, charlie, delta and actual.txt containing alpha, bravo, CHARLIE, delta, extra (one word per line).
Question 1. In the default output, which file does < refer to? Which does >?
Question 2. In a unified diff, what do a leading space, a - and a + each mean?
Question 3. Decode @@ -1,4 +1,5 @@ completely.
Question 4. Why does a changed line appear as a - line followed by a + line, rather than having its own marker?
Question 5. Give the three exit statuses of diff and what each means. Which other command in this chapter uses the same idea?
Question 6. diff a.txt b.txt prints nothing. What does that tell you, and why is it not a failure?
Question 7. Two files look identical on screen but diff reports every line as changed. Give the most likely cause and the chapter that diagnoses it.
Question 8. You want to know only whether a transferred binary arrived intact, with no output. Write the command and say which value you would check.
Question 9. You swap the order of the two file names. What changes in the output, and what does not?
10. Remember
diffreports how two text files differ;wccould only tell you that they do.<and-are the first file;>and+are the second.diffhas no opinion about which is correct — convention is old first, new second.- The unified format (
-u) is the one to learn, because it is what every patch, code review and version-control tool displays. - In a unified diff: leading space is context,
-is the first file only,+is the second only,@@gives the location. A change is a-plus a+. @@ -1,4 +1,5 @@means four lines from line 1 of the first file, five from line 1 of the second.- Exit status carries the answer:
0identical,1differ,2error. The first command in this course whose status is information rather than just success. - Identical files produce no output. Silence is the good result.
-qfor just the verdict,-wto ignore whitespace,-rfor whole directories — and-wcan hide a real problem, so use it knowingly.cmpcompares bytes and suits binary files;cmp -sanswers "identical?" through the status alone.- Different line endings make every line look changed. That is the next chapter.
One chapter of Module 4 remains, and it answers the question that has now come up three times: what kind of file is this, and why do two files that look identical refuse to behave the same way?
Where this fits
Part of the Linux & Bash curriculum.
