Comparing old log snapshots or configuration exports gets awkward when the files are still compressed with bzip2. The bzdiff command in Linux compares the decompressed content of .bz2 files without making you unpack them by hand, so you can keep archived files in place while checking exactly what changed.
bzdiff is a wrapper around diff from the bzip2 package. It is useful for text-like compressed files such as rotated logs, config snapshots, SQL exports, and release notes. It compares content after decompression, not the compressed bytes on disk, so checksum and byte-for-byte archive checks still belong to tools such as cmp or sha256sum.
Use bzdiff Command Syntax and Common Patterns
bzdiff invokes diff against bzip2-compressed input. It decompresses one or both files for comparison, passes supported diff options through, and returns the same meaningful exit statuses that diff uses.
bzdiff [diff_options] file1 [file2]
[diff_options]: Options passed todiff, such as-ufor unified output,-qfor quiet comparison, or-wto ignore whitespace.file1: The first file, normally a.bz2file.[file2]: The second file. If omitted,bzdiffcomparesfile1with the matching uncompressed filename when that file exists.
The official bzip2 manual documents the bzip2 utility family. For quick use, these patterns cover the most common bzdiff workflows:
| Task | Command Pattern | What It Does |
|---|---|---|
| Compare two bzip2 files | bzdiff old.txt.bz2 new.txt.bz2 | Prints normal diff output after decompression. |
| Show unified output | bzdiff -u --label=old.txt --label=new.txt old.txt.bz2 new.txt.bz2 | Uses unified diff format with stable labels. |
| Check quietly | bzdiff -q old.txt.bz2 new.txt.bz2 | Returns a status code without printing the full diff. |
| Use the matching plain file | bzdiff old.txt.bz2 | Compares old.txt.bz2 with old.txt when the plain file exists. |
| Show side-by-side changes | bzdiff -y --suppress-common-lines --width=120 old.txt.bz2 new.txt.bz2 | Shows changed lines in two columns. |
| Save a report | bzdiff -u old.txt.bz2 new.txt.bz2 > changes.diff | Writes a patch-style report while preserving bzdiff status codes. |
| Ignore whitespace and blanks | bzdiff -w -B old.txt.bz2 new.txt.bz2 | Ignores spacing-only and blank-line-only changes. |
| Ignore matching lines | bzdiff -I'^#' old.txt.bz2 new.txt.bz2 | Ignores changed hunks made only of matching comment lines. |
| Compare compressed with plain text | bzdiff old.txt.bz2 old.txt | Compares a compressed file with an uncompressed copy. |
| Compare decompressed bytes quietly | bzcmp -s old.txt.bz2 copy.txt.bz2 | Uses the companion bzcmp wrapper for byte-stream equality. |
When a
diffoption takes a value, use an equals or joined form withbzdiff, such as--label=old.txtor-I'^#'. A separated value can be interpreted as a filename by the wrapper.
Install or Verify bzdiff on Linux
The bzip2 package provides bzdiff, bzcmp, bzcat, bunzip2, and related helpers. Many desktop installations already include it, while minimal servers and containers may need the package installed first.
Check whether the command is already available:
command -v bzdiff
A common command path is:
/usr/bin/bzdiff
If the command is missing, install the bzip2 package for your distribution.
Ubuntu and Debian-Based Distributions
sudo apt update
sudo apt install bzip2
Fedora and RHEL-Family Distributions
sudo dnf install bzip2
Arch Linux and Manjaro
sudo pacman -S bzip2
openSUSE Tumbleweed
sudo zypper install bzip2
On openSUSE Leap, search your enabled repositories before adding community or experimental package sources for this small utility.
After installation, repeat command -v bzdiff. If it still returns nothing, open a new shell and check that /usr/bin is present in $PATH.
Create a Disposable bzdiff Practice Directory
A small practice directory keeps the examples safe and repeatable. Create two similar text files, preserve an identical copy, then compress all three with bzip2 -k. The -k option keeps the original uncompressed files so later examples can compare compressed and plain-text content.
mkdir -p ~/bzdiff-demo
cd ~/bzdiff-demo
cat > old.txt <<'EOF'
port=8080
mode=production
cache=true
EOF
cat > new.txt <<'EOF'
port=8080
mode=staging
cache=true
EOF
cp old.txt old-copy.txt
rm -f -- old.txt.bz2 new.txt.bz2 old-copy.txt.bz2
bzip2 -k old.txt new.txt old-copy.txt
ls -1 *.bz2
The directory now contains three compressed files:
new.txt.bz2 old-copy.txt.bz2 old.txt.bz2
Compare bzip2 Files with bzdiff
Start with the plain comparison form when you want compact output and do not need script-friendly labels.
Compare Two Compressed Text Files
Compare the two compressed config snapshots:
bzdiff old.txt.bz2 new.txt.bz2
The output uses normal diff notation. The change marker shows line 2 moving from production to staging:
2c2 < mode=production --- > mode=staging
A difference returns exit status 1. That status means the files differ; it is not the same as a broken command.
Show Unified Diff Output with Stable Labels
Use unified output when the result needs to fit review comments, patches, or readable change logs. Add labels with equals-style --label=VALUE options so the wrapper passes each label to diff correctly:
bzdiff -u --label=old.txt --label=new.txt old.txt.bz2 new.txt.bz2
--- old.txt +++ new.txt @@ -1,3 +1,3 @@ port=8080 -mode=production +mode=staging cache=true
Without labels, unified output can show temporary paths created by the wrapper. Labels keep the output stable and easier to paste into tickets or code reviews.
Compare a Compressed File with Plain Text
bzdiff can compare a compressed file with an uncompressed file. This is useful after extracting or editing a local copy:
bzdiff -q old.txt.bz2 old.txt && echo "Compressed and uncompressed contents match"
Compressed and uncompressed contents match
The -q option keeps the comparison quiet when the files match. Remove -q when you want the full diff.
Compare a Compressed File with Its Matching Plain File
When the second file is omitted, bzdiff compares the compressed file with the matching uncompressed name. With old.txt.bz2, the implicit comparison target is old.txt:
bzdiff -q old.txt.bz2 && echo "Compressed file matches old.txt"
Compressed file matches old.txt
This shortcut is convenient after bzip2 -k, because the original file stays beside the compressed copy. If the plain file is missing, diff returns a real error instead of silently comparing against nothing.
Show Side-by-Side bzdiff Output
Side-by-side output is easier to scan when only a few lines changed. Use joined or equals-style values for options that take arguments, such as --width=72:
bzdiff -y --suppress-common-lines --width=72 old.txt.bz2 new.txt.bz2
Relevant output keeps the unchanged lines hidden and shows the changed setting in two columns:
mode=production | mode=staging
Use a larger --width value for long configuration lines. If the output wraps in your terminal, redirect the report to a file or switch back to unified output with -u.
Save a bzdiff Report to a File
Saving a unified report is useful for tickets, backup audits, and deployment notes. Capture the status separately because a saved diff still exits with status 1 when differences exist:
bzdiff -u --label=old.txt --label=new.txt old.txt.bz2 new.txt.bz2 > config.diff
status=$?
if [ "$status" -eq 1 ]; then
echo "Saved differences to config.diff"
elif [ "$status" -ne 0 ]; then
echo "bzdiff failed with status $status" >&2
fi
Saved differences to config.diff
Inspect the report with less config.diff or attach it to a change record. Avoid chaining the redirect with && when changed content is a normal result.
Check bzdiff Exit Status in Scripts
Scripts need to treat status 1 as a real comparison result. Capture the status and branch on it instead of assuming any nonzero status is fatal:
if bzdiff -q old.txt.bz2 new.txt.bz2 >/dev/null; then
echo "No differences"
else
status=$?
if [ "$status" -eq 1 ]; then
echo "Files differ"
else
echo "bzdiff failed with status $status" >&2
fi
fi
Files differ
Use this pattern in backup audits or deployment checks where changed content should trigger a report, while missing files or corrupt input should fail the job.
Ignore Whitespace and Blank-Line Changes
diff -w ignores whitespace while comparing lines, and -B ignores changes that are only blank lines. Both options work through bzdiff:
cat > spaces-a.txt <<'EOF'
key=value
path=/srv/app
EOF
cat > spaces-b.txt <<'EOF'
key = value
path = /srv/app
EOF
rm -f -- spaces-a.txt.bz2 spaces-b.txt.bz2
bzip2 -k spaces-a.txt spaces-b.txt
bzdiff -w -B -q spaces-a.txt.bz2 spaces-b.txt.bz2 && echo "Only whitespace or blank lines changed"
Only whitespace or blank lines changed
This is useful for generated configuration files where formatting changed but the meaningful key/value data did not. Do not use these options when indentation or blank lines carry meaning for the file format you are reviewing.
Ignore Generated Comment Lines
Use -I when every changed line in a hunk matches a pattern, such as generated timestamp comments. Keep the pattern joined to -I so the wrapper does not treat it as a filename:
cat > comments-a.txt <<'EOF'
# generated=100
port=8080
mode=production
EOF
cat > comments-b.txt <<'EOF'
# generated=200
port=8080
mode=production
EOF
rm -f -- comments-a.txt.bz2 comments-b.txt.bz2
bzip2 -k comments-a.txt comments-b.txt
bzdiff -q -I'^#' comments-a.txt.bz2 comments-b.txt.bz2 && echo "Only generated comments changed"
Only generated comments changed
Use this only when comment-only differences are safe to ignore. If a hunk includes both a comment and a real setting change, diff -I still reports the hunk.
Compare Matching Compressed Files Across Directories
bzdiff expects regular files, so it does not recurse through directories like diff -r. For compressed snapshot folders, build a controlled file list and compare matching relative paths:
mkdir -p old-set/app new-set/app
printf 'alpha\n' > old-set/app/same.conf
printf 'alpha\n' > new-set/app/same.conf
printf 'mode=prod\n' > old-set/app/changed.conf
printf 'mode=stage\n' > new-set/app/changed.conf
printf 'only-old\n' > old-set/app/missing.conf
find old-set new-set -type f -name '*.bz2' -delete
find old-set new-set -type f -name '*.conf' -exec bzip2 -k {} \;
find old-set -type f -name '*.bz2' | sort | while IFS= read -r old_path; do
rel=${old_path#old-set/}
new_path="new-set/$rel"
if [ ! -f "$new_path" ]; then
printf 'MISSING %s\n' "$rel"
continue
fi
bzdiff -q "$old_path" "$new_path" >/dev/null
status=$?
case "$status" in
0) printf 'SAME %s\n' "$rel" ;;
1) printf 'DIFF %s\n' "$rel" ;;
*) printf 'ERROR %s status=%s\n' "$rel" "$status" ;;
esac
done
DIFF app/changed.conf.bz2 MISSING app/missing.conf.bz2 SAME app/same.conf.bz2
This pattern is safer than running a broad comparison over every file in both trees because it names missing files separately and keeps status 1 as a normal difference result.
Compare Filtered Content from bzip2 Logs
bzdiff compares the whole decompressed file. Use Bash process substitution with bzcat when you need to filter output with grep before comparing only matching lines:
cat > old.log <<'EOF'
INFO startup
ERROR cache stale
INFO done
EOF
cat > new.log <<'EOF'
INFO startup
ERROR cache refreshed
INFO done
EOF
rm -f -- old.log.bz2 new.log.bz2
bzip2 -k old.log new.log
diff -u --label=old-errors --label=new-errors \
<(bzcat old.log.bz2 | grep '^ERROR') \
<(bzcat new.log.bz2 | grep '^ERROR')
--- old-errors +++ new-errors @@ -1 +1 @@ -ERROR cache stale +ERROR cache refreshed
This is no longer a pure bzdiff command, but it solves a common compressed-log problem without unpacking either file. Use it in Bash; plain POSIX sh does not support process substitution.
Use bzcmp for Quiet Byte-Stream Checks
The companion bzcmp command compares decompressed byte streams through cmp. It is a better fit when you only need equality and not line-oriented diff output:
bzcmp -s old.txt.bz2 old-copy.txt.bz2 && echo "Decompressed byte streams match"
Decompressed byte streams match
Compare Content Instead of Compressed Bytes
bzdiff answers a content question: do these files decompress to the same text? It does not prove the compressed archives are byte-for-byte identical. Compression settings can create different .bz2 files from the same input.
cp old.txt same-low.txt
cp old.txt same-high.txt
rm -f -- same-low.txt.bz2 same-high.txt.bz2
bzip2 -1 -k same-low.txt
bzip2 -9 -k same-high.txt
cmp -s same-low.txt.bz2 same-high.txt.bz2 || echo "Compressed files differ"
bzdiff -q same-low.txt.bz2 same-high.txt.bz2 && echo "Decompressed contents match"
Compressed files differ Decompressed contents match
Use bzdiff for content review. Use cmp or sha256sum when transfer integrity, cache identity, or exact archive bytes matter.
Use the Right Tool for Other Archive Formats
bzdiff is specific to bzip2 streams. If a file uses .gz, .tgz, or .tar.gz, use gzip and tar workflows instead; the GZ and TGZ extraction guide explains those formats. ZIP archives are a separate container format, so use unzip command examples when the file ends in .zip.
A .tar.bz2 file is a tar archive compressed as one bzip2 stream. The same boundary applies to shorthand names such as .tbz2 and .tbz. bzdiff can tell you some tar streams differ, but it does not compare archive members by filename. List the archive first when you only need to confirm what it contains:
tar -tjf old-backup.tar.bz2
For member-by-member review, extract both archives into empty disposable directories, then compare those directories with normal diff:
mkdir -p old-backup new-backup
tar -xjf old-backup.tar.bz2 -C old-backup
tar -xjf new-backup.tar.bz2 -C new-backup
diff -ru old-backup new-backup
When you are unsure about a file, inspect it before choosing a comparison tool:
file old.txt.bz2
old.txt.bz2: bzip2 compressed data, block size = 900k
A bzip2 file reports bzip2-compressed data. A misleading extension is a common reason bzdiff fails before it reaches diff.
Troubleshoot Common bzdiff Errors
Most bzdiff failures come from command availability, wrapper argument parsing, regular-file limits, invalid bzip2 data, or scripts that treat a normal difference status as fatal.
Fix bzdiff Command Not Found
If your shell cannot find bzdiff, check the command path first:
command -v bzdiff || echo "bzdiff is missing"
Install bzip2 with your distro package manager, then repeat the check. The package name is bzip2, not bzdiff.
Fix Option Values Treated as Filenames
A separated option value can make bzdiff stop before it compares the intended files. For example, this form passes ^# as a separate argument:
bzdiff -q -I '^#' comments-a.txt.bz2 comments-b.txt.bz2
bzdiff: ^# not found or not a regular file
Attach the value to the option, or use an equals form when the long option supports one:
bzdiff -q -I'^#' comments-a.txt.bz2 comments-b.txt.bz2
bzdiff -u --label=old.txt --label=new.txt old.txt.bz2 new.txt.bz2
Fix Missing Plain File Errors
The one-file form depends on the matching uncompressed file. If old.txt is missing, bzdiff old.txt.bz2 reaches diff and fails:
rm -f old.txt
bzdiff -q old.txt.bz2
diff: old.txt: No such file or directory
Restore the plain file when you want to compare the compressed copy against its original name:
bunzip2 -k old.txt.bz2
bzdiff -q old.txt.bz2 && echo "Compressed file matches old.txt"
Compressed file matches old.txt
If you meant to compare against a different file, give that second filename explicitly instead of relying on the one-file shortcut.
Fix Directory Arguments Not Regular File
bzdiff does not recurse through directories. Passing directory names, even with a normal diff option such as -r, stops at the wrapper:
bzdiff -r old-set new-set
bzdiff: old-set not found or not a regular file
Compare directory snapshots by iterating over regular .bz2 files and matching relative paths. A safe loop should report same, different, missing, and error states separately.
Fix Usage Output with Filenames That Contain Spaces
Some packaged bzdiff wrappers split the file list internally, so shell quoting does not reliably save pathnames with spaces. A quoted command can still collapse to a usage error:
printf 'left\n' > 'old space.txt'
printf 'right\n' > 'new space.txt'
rm -f -- 'old space.txt.bz2' 'new space.txt.bz2'
bzip2 -k 'old space.txt' 'new space.txt'
bzdiff 'old space.txt.bz2' 'new space.txt.bz2'
Usage: bzdiff [diff_options] file [file]
Use diff with bzcat in Bash when you must compare files with spaces in their names:
diff -u --label="old space.txt" --label="new space.txt" \
<(bzcat "old space.txt.bz2") \
<(bzcat "new space.txt.bz2")
--- old space.txt +++ new space.txt @@ -1 +1 @@ -left +right
That fallback also works for generated streams, but it uses Bash process substitution. For portable shell scripts, decompress into a temporary directory with safe filenames, compare those files, and remove the temporary directory afterward.
Fix Bad Magic Number Errors
A file with a .bz2 extension is not necessarily valid bzip2 data. Test the archive before comparing it:
printf 'not bzip2 data\n' > bad.bz2
bzip2 -t bad.bz2
bzip2: bad.bz2: bad magic number (file not created by bzip2) You can use the `bzip2recover' program to attempt to recover data from undamaged sections of corrupted files.
Use the correct file or retrieve a fresh copy. If the file is a damaged bzip2 archive and you must salvage partial data, run bzip2recover in a clean directory and inspect the recovered pieces before trusting them.
Fix Scripts That Fail on Real Differences
Automation often runs with set -e, which exits on any nonzero status. Since bzdiff returns 1 for normal differences, wrap the command in an if statement or capture the status before the shell exits.
Use this interpretation when debugging a script: status 0 means no differences, status 1 means differences were found, and status 2 or another higher value means a real comparison problem such as a missing file, invalid archive, or unsupported option form.
Clean Up the bzdiff Demo Directory
Remove only the practice directory created for these examples when you no longer need the compressed fixtures:
cd ~
rm -rf ~/bzdiff-demo
Conclusion
bzdiff is ready for compressed-content reviews when you can compare .bz2 files, save reports, filter inputs safely, handle exit status 1 correctly, and avoid wrapper traps around directories, option values, and filenames with spaces. Keep bzcmp for quiet byte-stream matches, and use tar or format-specific tools when the archive is not a plain bzip2 stream.


Formatting tips for your comment
You can use basic HTML to format your comment. Useful tags currently allowed in published comments:
<code>command</code>command<strong>bold</strong><em>italic</em><a href="https://example.com">link</a><blockquote>quote</blockquote>