diff Command in Linux: Complete Guide to File Comparison, Line Differences, and Parameters

diff command in Linux and it perimeters

I use diff almost every single day, usually without even thinking about it, because it’s baked into git, into config-management tools, and into every code review workflow I touch. But when I actually sat down to learn diff properly — not just the two or three flags I’d memorized — I realized how much more it could do: recursive directory comparisons, side-by-side views, unified patches I could email to a colleague, context diffs for old-school patch workflows. This guide covers all of that, from the absolute basics to the internals of how the comparison algorithm actually works.

What Is the diff Command?

diff compares two files (or two directories) line by line and reports the differences between them. It’s part of the GNU diffutils package, and it’s the foundation underneath tools you already use constantly: git diff, patch, code review tools, and configuration drift detectors all rely on the same core comparison logic that diff implements directly at the command line.

Basic Syntax

diff [OPTION]... FILE1 FILE2
diff [OPTION]... DIR1 DIR2

diff returns an exit status that scripts rely on constantly:

A Basic Example

Let’s set up two nearly-identical files:

$ printf 'line1\nline2\nline3\n' > file1.txt
$ printf 'line1\nlineX\nline3\nline4\n' > file2.txt
$ diff file1.txt file2.txt
2c2
< line2
---
> lineX
3a4
> line4

Reading this “normal” format output:

This normal format is the default, but it’s rarely what people actually want to read or share — the unified and context formats below are much more common in practice.

Unified Diff Format (-u)

This is the format you see in every git diff and almost every patch file on the internet:

$ diff -u file1.txt file2.txt
--- file1.txt	2026-07-31 01:36:41.930234377 +0000
+++ file2.txt	2026-07-31 01:36:41.930234377 +0000
@@ -1,3 +1,4 @@
 line1
-line2
+lineX
 line3
+line4

Here, lines prefixed with - were removed, lines prefixed with + were added, and unprefixed lines are unchanged context. The @@ -1,3 +1,4 @@ header tells you the hunk starts at line 1 spanning 3 lines in the original and 4 lines in the new file. This format is compact, human-readable, and directly consumable by patch and git apply.

Context Diff Format (-c)

An older, more verbose format that’s still used in some traditional Unix and BSD workflows:

$ diff -c file1.txt file2.txt
*** file1.txt	Fri Jul 31 01:36:41 2026
--- file2.txt	Fri Jul 31 01:36:41 2026
***************
*** 1,3 ****
  line1
! line2
  line3
--- 1,4 ----
  line1
! lineX
  line3
+ line4

Lines marked with ! indicate a change, + an addition, - a removal, and unmarked lines are context. Context diffs show more surrounding lines by default than unified diffs did historically, though modern unified diffs configured with -U N can match that.

Side-by-Side Format (-y)

When you want to eyeball two files next to each other rather than parse diff markers:

$ diff -y file1.txt file2.txt
line1								line1
line2							      |	lineX
line3								line3
								      >	line4

The | marks a changed line, and > marks a line only present in the second file. You can control the column width with -W N (e.g. diff -y -W 80), and --suppress-common-lines will hide identical lines so only the differences are shown — very useful for wide files where scrolling through unchanged content wastes screen space.

Full Parameter Reference

OptionLong FormDescription
-u [N]--unified[=N]Unified diff format, with N lines of context (default 3)
-c [N]--context[=N]Context diff format, with N lines of context
-y--side-by-sideSide-by-side column output
-q--briefOnly report whether files differ, not the details
-r--recursiveRecursively compare directories
-N--new-fileTreat absent files in one directory as empty during recursive compares
-i--ignore-caseIgnore case differences
-w--ignore-all-spaceIgnore all whitespace when comparing
-b--ignore-space-changeIgnore changes in amount of whitespace
-B--ignore-blank-linesIgnore changes where lines are all blank
-x PATTERN--exclude=PATTERNExclude files matching PATTERN in directory comparisons
-a--textTreat all files as text, even if they look binary
--color[=WHEN]Colorize output (always, auto, never)
-s--report-identical-filesReport when two files are the same
-W N--width=NSet output width for -y
--suppress-common-linesHide identical lines in -y output
-e--edOutput an ed script that transforms file1 into file2
-n--rcsOutput RCS-format diff

-q: Quick Brief Check

When you only care whether files differ, not the details, -q (or its alias --brief) is faster to read and script against:

$ diff -q file1.txt file2.txt
Files file1.txt and file2.txt differ

$ diff -q file1.txt file1.txt; echo "exit: $?"
exit: 0

Notice there’s no output at all when files are identical — only the exit code changes. This makes -q ideal for scripting conditionals.

-r: Recursive Directory Comparison

This is where diff becomes genuinely powerful for sysadmin work — comparing entire directory trees, like two versions of a config directory or a deployed application:

$ mkdir -p dir1 dir2
$ cp file1.txt dir1/
$ cp file2.txt dir1/common.txt
$ cp file2.txt dir2/common.txt
$ echo "hi" > dir1/onlyindir1.txt

$ diff -r dir1 dir2
Only in dir1: file1.txt
Only in dir1: onlyindir1.txt

Since common.txt is identical in both directories, it’s silently skipped — only differences and unique files are reported. Add -q to -r for a terse summary across a large tree, or drop -q to see full content diffs for every file that differs.

How diff Works Internally

diff‘s core job is solving what’s known in computer science as the longest common subsequence (LCS) problem. Given two sequences of lines, the goal is to find the longest sequence of lines that appears, in order, in both files — everything else is then classified as either removed (present only in file1) or added (present only in file2).

GNU diff implements a variant of the Myers diff algorithm, published by Eugene Myers in 1986, which finds an optimal (shortest) edit script in O(ND) time, where N is the sum of the lengths of the two files and D is the size of the edit script (the number of differing lines). This is dramatically faster than a naive O(N²) or O(N³) dynamic-programming LCS approach for files that are mostly similar — which is the overwhelmingly common case (patches, revisions, config tweaks).

The algorithm works conceptually by modeling the comparison as a graph traversal problem: each point in an edit graph represents a partial alignment between the two files, and the algorithm searches for the shortest path from the top-left corner (start of both files) to the bottom-right corner (end of both files), where diagonal moves represent matching lines (free) and horizontal/vertical moves represent insertions or deletions (cost 1). Myers’ algorithm efficiently prunes this search using a greedy, breadth-first-like expansion by edit distance, which is why diff stays fast even on files with thousands of lines.

Once the edit script is computed, diff formats it according to whichever output style you requested — normal, unified, context, side-by-side, or ed script — by grouping consecutive changes into “hunks” with configurable amounts of surrounding context.

Ignoring Whitespace and Case

Real-world files often differ only in trivial ways — trailing whitespace, indentation style, or capitalization — that you don’t actually care about:

$ diff -w file_with_spaces.txt file_without_spaces.txt   # ignore all whitespace
$ diff -b file1.txt file2.txt                             # ignore amount of whitespace change
$ diff -i file1.txt file2.txt                             # ignore case
$ diff -B file1.txt file2.txt                             # ignore blank-line-only changes

Combining these is common in real workflows, e.g. diff -wBi to strip out noise and focus purely on substantive content changes.

Generating and Applying Patches

diff -u output is exactly what patch and git apply expect. A typical patch workflow:

$ diff -u original.conf modified.conf > changes.patch
$ patch original.conf < changes.patch

For entire directory trees, add recursive comparison with proper relative paths using -Nur (new-file, unified, recursive) — this is the classic incantation for generating a patch that can be applied cleanly against a fresh checkout:

$ diff -Nur project-v1/ project-v2/ > project-v1-to-v2.patch

Real-World Use Cases

1. Configuration Drift Detection

Comparing a live server’s config against a known-good baseline is one of the most common sysadmin uses:

$ diff -u /etc/nginx/nginx.conf /backups/nginx.conf.baseline

2. Pre-Deployment Review

Before pushing a config change, diffing the staged version against production catches unintended edits:

$ diff -u /etc/app/config.prod.yaml /etc/app/config.staged.yaml

3. Verifying File Integrity After Transfer

After scp or rsync, a quick diff -q confirms nothing got corrupted in transit:

$ diff -q original.tar.gz copied.tar.gz && echo "Transfer verified OK"

4. Auditing Package or System Changes

Comparing directory snapshots before and after a package installation reveals exactly what changed:

$ diff -rq /etc.before /etc.after

5. Code Review Without git

Sometimes you just have two versions of a script on disk, no repo involved:

$ diff -u deploy.sh.old deploy.sh.new | less

Shell Scripting and Automation

Here’s a small script I use to detect and alert on configuration drift across a fleet of servers, using diff‘s exit status directly in a conditional:

#!/bin/bash
# check_config_drift.sh - compare live config against baseline
set -euo pipefail

BASELINE="/opt/baselines/nginx.conf"
LIVE="/etc/nginx/nginx.conf"

if diff -q "$BASELINE" "$LIVE" > /dev/null; then
    echo "OK: nginx.conf matches baseline"
    exit 0
else
    echo "WARNING: nginx.conf has drifted from baseline"
    diff -u "$BASELINE" "$LIVE"
    exit 1
fi

This pattern — if diff -q A B > /dev/null; then ... else ... fi — is one of the most common idioms in configuration-management and monitoring scripts, because it lets you branch on file equality cheaply and only pay the cost of generating a full diff when something has actually changed.

diff vs Related Commands

CommandPurpose
diffLine-by-line comparison of two files or directories
cmpByte-by-byte comparison; reports the first differing byte, useful for binary files
commCompares two sorted files line by line, showing lines unique to each and lines common to both
git diffWraps the same diff algorithm but adds version-control context (staged/unstaged, commit ranges)
vimdiff / meldVisual, interactive diff tools built on the same underlying comparison logic
patchConsumes diff output to apply changes to a file
rsync -n --itemize-changesDirectory-level comparison focused on sync operations rather than content diffing

The key distinction to remember: cmp is for binary-safe, byte-level comparison and stops at the first difference; diff is for text, line-oriented comparison and reports the complete set of differing regions.

Troubleshooting Common Issues

Problem: diff reports differences but the files look identical on screen. This is almost always whitespace — trailing spaces, tabs vs. spaces, or line-ending differences (\r\n vs \n). Try diff -w or check line endings with file file1.txt file2.txt (which reports “CRLF line terminators” if present) or cat -A.

Problem: diff -r reports “Only in dirX” for files that should be common. Confirm the filenames genuinely match, including case — diff is case-sensitive by default even with -r. Also verify you’re not being tripped up by symlinks that diff treats differently depending on whether -L or --no-dereference-style handling is in play.

Problem: diff treats a text file as binary and won’t show line-level differences. If a file contains a NUL byte or other binary-like content, diff may default to “Binary files … differ.” Force text-mode comparison with -a.

Problem: Comparing huge files is slow. diff‘s Myers algorithm is efficient for typical edit distances, but a worst-case scenario (two files with almost nothing in common) can degrade toward higher complexity. For huge, mostly-dissimilar files, consider whether you actually need a full diff, or whether a checksum comparison (sha256sum) is sufficient to confirm they differ.

Performance Optimization

For most files, diff is fast enough that performance tuning is unnecessary. For very large files or high-volume automated comparisons:

Security Implications

diff reads file contents and, when given directory arguments, traverses the filesystem — so the standard cautions apply: be careful comparing directories that might contain symlinks pointing outside the intended tree (diff -r follows symlinks by default in most configurations, which can leak or expose content from unexpected locations). When generating patches from sensitive files for review or transfer, remember the resulting diff contains the actual content of both files’ differing regions — treat patch files with the same sensitivity as the source files themselves. diff does not execute any code from its input, so it’s safe to run against untrusted files from a code-execution standpoint, but the output of a diff can itself be fed into patch, which does modify files — always review a patch before applying it from an untrusted source.

Compatibility Across Distributions

diff is part of GNU diffutils and is present by default on virtually every Linux distribution — Ubuntu, Debian, Fedora, RHEL/CentOS, Arch, openSUSE. The behavior described here is consistent across GNU-based systems. macOS and BSD systems ship a different, POSIX-oriented diff implementation with a smaller feature set (no --color, more limited GNU-style long options), so scripts intended to be portable across GNU/BSD should stick to POSIX-standard flags (-u, -c, -r, -i, -b) and test on both platforms if that matters.

Best Practices

Summary

diff is one of the oldest and most quietly essential tools in the Linux ecosystem, implementing an efficient LCS-based comparison (via the Myers algorithm) that underlies everything from git to configuration management to code review tooling. Once you’re comfortable with unified format, recursive directory comparison, and the whitespace-handling flags, you have a tool that scales from “did this one file change” all the way up to full patch generation across entire project trees. It’s worth knowing well beyond the two or three flags most people memorize from muscle memory.

References

Exit mobile version