How to Compare Two Files and Show Only Differences
Compare two files and show only the differences with diff, cmp, comm and git diff, in unified, side-by-side and word-level views, and ignore whitespace.

Comparing two files is a daily need: which config changed, what a tool rewrote, whether two exports match. The shell offers several tools, each good at one view. The examples use GNU diffutils 3.10 and Git 2.45; the longer treatment of the format is in unified diff and patches.
Are they different at all
diff -q old.conf new.conf
cmp old.bin new.bin
cmp -s old.conf new.conf && echo identical
echo "status $?"Files old.conf and new.conf differ
old.bin new.bin differ: byte 1042, line 17
identical
status 0
diff -q only reports whether the files differ. cmp reports the first differing byte and line, and is the right tool for binary files, since diff works on lines. cmp -s prints nothing and sets the exit status, which is what scripts need. The status is 0 for identical, 1 for different and 2 for an error such as a missing file, so test for 1 specifically when you need to tell a difference from a failure.
Show the differences
diff -u old.conf new.conf--- old.conf 2026-11-02 18:01:44
+++ new.conf 2026-11-03 09:12:10
@@ -1,4 +1,4 @@
listen 80;
-server_name example.com;
+server_name example.org;
root /var/www;
index index.html;
Lines starting with - are only in the first file and lines starting with + only in the second; unchanged context lines have a space. Unified format is the one patch tools and code review expect. Add -r to compare two directories recursively, and --color=auto to colour the output on a terminal.
Side by side and word level
diff -y --suppress-common-lines -W 100 old.conf new.conf
git diff --no-index --word-diff=color old.txt new.txt-y prints the two files in columns, and --suppress-common-lines hides identical lines, which keeps the output short. git diff --no-index works outside a repository and offers --word-diff, which marks changed words rather than whole lines, useful for prose and long lines; the options are in the word diff guide. vimdiff and delta give interactive and highlighted views, as in delta and difftastic.
Ignore noise
diff -u -b old.txt new.txt # ignore changes in amount of whitespace
diff -u -w old.txt new.txt # ignore all whitespace
diff -u -B old.txt new.txt # ignore blank-line changes
diff -u --strip-trailing-cr win.txt unix.txt--strip-trailing-cr removes the carriage returns that differ between Windows and Unix files, which otherwise make every line look changed; see changing line endings. For files whose order does not matter, sort both first, or use comm on the sorted copies to see lines unique to each, covered in comparing sets of lines with comm.
Compare against a command's output
diff -u <(ssh web1 cat /etc/hosts) <(ssh web2 cat /etc/hosts)
diff -u expected.txt <(./tool --list)Process substitution lets you compare outputs without temporary files, which is how to check two servers or a program against a known-good result.
Choosing the right level of comparison
Different questions need different tools. "Are these two files the same?" is a yes or no answer, and a checksum or cmp -s gives it in one step and works on huge files, since cmp stops at the first difference. "What changed?" needs diff, which computes a minimal edit script and reads both files into memory, so it slows down on files of hundreds of megabytes. "Which lines are in one but not the other, regardless of order?" is a set question, and comm answers it on sorted input: comm -23 <(sort a) <(sort b) prints lines only in the first. Picking the tool by the question avoids reading a thousand-line diff when a one-line check was needed.
Directories and trees
diff -r dir1 dir2 compares two trees recursively and reports files that exist on only one side, and -q keeps it to names. Add --exclude patterns to skip caches and build output, and -N to treat missing files as empty, so that the output shows the full content of additions. For a faster check of a large tree, compare checksum listings: (cd a && find . -type f -exec sha256sum {} + | sort -k2) > a.sum, the same for b, and diff the two lists. That also works across machines when only the lists can be moved, which is the usual case when checking a deployment against a build.
Merging and patching
A unified diff is also a patch. Save it with diff -u old new > change.patch and apply with patch -p0 < change.patch, or with git apply. The patch format stores context lines, so it can apply to a file that changed elsewhere, and it fails clearly when the context no longer matches, leaving a reject file with the pieces that did not fit. For three-way merges, diff3 and git merge-file take a common ancestor and two modified versions and write the merged result with conflict markers where the changes overlap. The ancestor matters, since two-way comparison cannot tell which side made a change.
Readable output for review
Colour helps: diff --color=auto -u or git diff --no-index --color. For prose and configuration with long lines, a word-level or character-level view is more useful than a line view, because a one-word change otherwise marks the entire line. Tools such as delta, diff-so-fancy and difftastic add syntax awareness and word highlighting, and the last compares by syntax tree, so a reformatted function does not show as a rewrite. Use vimdiff or an editor's diff view when resolving differences by hand, because moving a change from one side to the other is easier with both files visible.
Comparing sorted data and sets
Many comparisons are really about sets of lines: users in one export and not another, hosts in two inventories, packages installed on two machines. comm takes two sorted files and prints three columns: lines only in the first, only in the second, and in both. The flags suppress columns, so comm -12 prints the intersection and comm -13 prints what is new in the second file. Sorting must match the locale used by comm, so run both sort and comm under the same LC_ALL, usually C. If the sort order differs, comm reports a problem about unsorted input, or worse, gives a wrong answer silently in older versions.
For tabular data with a key, join merges two sorted files on a field, and awk with an associative array does the same without sorting. Pick the one that matches the size of the data: the array approach holds one file in memory, which is fine for hundreds of thousands of lines and a problem at hundreds of millions.
Exit statuses for scripts
All the tools report the result in the exit status: diff and cmp return 0 when the files are identical, 1 when they differ and 2 on trouble such as a missing file. A script that tests only for zero treats an error as a difference. Check for the value 2 explicitly when the distinction matters, and send the output to a file or to /dev/null when only the status is wanted.
Where this does not apply
Line-based tools treat a moved block as a delete and an add, and a reflowed paragraph as a full rewrite. Structured formats are better compared with a structural tool: jq -S . a.json sorted and then diffed for JSON, or a dedicated XML or YAML diff. Very large files are slow to diff and better handled with cmp or hashes. See the GNU diffutils manual and the git diff documentation.


