Compare Files Safely with diff on Linux
You will finish with a reliable way to compare two files or directory trees, read the useful parts of the output, and use the exit status in scripts. The examples use GNU diffutils 3.10, installed here as diff at /usr/bin/diff.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about fifteen minutes. You need a shell and two files you are allowed to read. The ordinary comparisons below do not need sudo. This guide does not change either input file, but be careful with shell redirection: writing diff output to an existing file can truncate it.
1. Check the installed command
Confirm the binary and version before relying on an option in a script. This is read-only:
$ command -v diff
/usr/bin/diff
$ diff --version
diff (GNU diffutils) 3.10
The local manual describes the normal output as the default. It accepts two files, two directories, or one file and one directory. A single hyphen means standard input, which is useful for a generated or piped document.
Checkpoint: if command -v diff returns a different path, use that installation's version and manual when writing portable automation.
2. Read a normal file comparison
Create two temporary example files, or substitute paths to your own files. The command reads both files and prints the changes:
$ diff /path/to/old.txt /path/to/new.txt
2c2,3
< line two
---
> line changed
> new line
In this form, 2c2,3 says that line 2 in the first file should be changed to lines 2 through 3 in the second. A line beginning with < came from the first operand; a line beginning with > came from the second. The three hyphens separate the sides of the change.
Do not treat a non-zero result as an automatic failure yet. diff returns 0 for identical inputs, 1 when they differ, and 2 when it cannot complete the comparison because of trouble such as a missing file or an access error.
3. Use unified output for reviews and patches
Unified output is usually easier to review because it puts the old and new lines into one context. Add -u, or use --unified:
$ diff -u /path/to/old.txt /path/to/new.txt
--- /path/to/old.txt 2026-09-23 02:37:42.846885271 +0100
+++ /path/to/new.txt 2026-09-23 02:37:42.846885271 +0100
@@ -1,2 +1,3 @@
same
-line two
+line changed
+new line
The timestamps are taken from the files and will differ on your machine. Lines beginning with a space are context, a minus line is removed from the first file, and a plus line is added in the second. The header identifies the operand order, so keep the old file first when the output will be reviewed or passed to another tool.
Three context lines are the default. Use -U 0 for no context or -U 5 for five lines when that makes a review clearer. More context can make a small change easier to locate, but it also makes output longer.
4. Decide whether differences matter
If you only need a yes-or-no answer, use -q or --brief:
$ diff -q /path/to/old.txt /path/to/new.txt
Files /path/to/old.txt and /path/to/new.txt differ
$ printf 'exit status: %s\n' "$?"
exit status: 1
For an identical pair, -q prints nothing and exits 0. Use -s or --report-identical-files when you want confirmation in a human-facing check:
$ diff -s /path/to/old.txt /path/to/old.txt
Files /path/to/old.txt and /path/to/old.txt are identical
In a shell script, handle all three states explicitly:
if diff -q "$left" "$right" >/dev/null; then
printf '%s\n' 'same'
elif [ "$?" -eq 1 ]; then
printf '%s\n' 'different'
else
printf '%s\n' 'comparison failed' >&2
exit 2
fi
The second test works because the status from the immediately preceding diff is still available. Do not replace it with a generic test for any non-zero status if an operational error must be distinguished from an expected difference.
5. Compare directories without drowning in output
Pass two directory paths and add -r or --recursive:
$ diff -ru /path/to/old-tree /path/to/new-tree
diff -ru /path/to/old-tree/config.ini /path/to/new-tree/config.ini
--- /path/to/old-tree/config.ini 2026-09-23 02:37:42.853885189 +0100
+++ /path/to/new-tree/config.ini 2026-09-23 02:37:42.854885177 +0100
@@ -1,2 +1,3 @@
mode=quiet
-retries=2
+retries=3
+timeout=30
The -u and -r options are independent: -r walks matching subdirectories, while -u selects the display format. Add -q instead of -u when you only need to know whether the trees differ.
Directory comparisons also report files present on one side only. Review those names before using the result to remove or synchronise anything. diff itself does not copy, delete or merge the files.
6. Filter harmless formatting noise deliberately
Text files can differ in presentation rather than meaning. Choose the narrowest rule that matches your goal:
-iignores case differences.-bignores changes in the amount of whitespace.-wignores all whitespace.-Bignores changes where lines are blank.-Zignores whitespace at the end of a line.-Eignores changes caused by tab expansion.
These options can hide a real change. For example, -i makes Admin and admin compare equal even when a case-sensitive program treats them as different. Start with plain diff -u, then add one justified filter and record it in the review or script.
For files that are not plain text, use -a or --text only when treating the bytes as text is appropriate. Do not infer that two binary files are equal from a short display; use the exit status, and consider cmp when a byte-for-byte comparison is the actual requirement.
7. Avoid accidental data loss
diff is read-only, but this command is not safe when the output path is one of its inputs:
$ diff -u old.txt new.txt > old.txt
The shell opens and truncates old.txt before diff reads it. That damage is not reversible through diff. Stop before running a redirection if the destination could be an input. Write to a new path, inspect it, and replace the old file only after a separate backup and review:
$ diff -u old.txt new.txt > comparison.patch
$ less comparison.patch
$ cp --preserve=all old.txt old.txt.bak
$ mv comparison.patch reviewed.patch
The example leaves both original inputs untouched. If you later apply a patch, treat that as a separate, state-changing operation and keep the backup until the result has been tested. No command here requires elevated privileges; use sudo only when file permissions genuinely require it, and keep the comparison itself as an unprivileged operation where possible.
Done means
- You checked that the installed command is GNU diffutils 3.10 or identified the version you are actually using.
- You can read normal and unified output, including the operand order.
- Your script distinguishes identical, different and failed comparisons.
- You use recursive comparison for directory trees and filter whitespace only for a stated reason.
- You keep diff output in a new file and never redirect it over an input.