Comparing Files and Directory Trees with filecmp
Compare file contents and directory trees with filecmp, understand shallow comparisons and caching, and separate mismatches from comparison errors.
Intermediate
Python’s filecmp module compares file contents and directory trees without launching an external diff program. Use it when a program needs to classify matching files, identify changed names, or summarize differences between two directory snapshots. It is not a replacement for pathlib when locating paths, shutil when copying them, or difflib when a human-readable text diff is needed.
Compare file contents deliberately
filecmp.cmp(first, second, shallow=False) compares the file contents when the files have different sizes or the comparison cannot be settled by the shallow shortcut. The default shallow=True treats files as equal when their stat signatures (file type, size, and modification time) match. That is a performance trade-off, not proof that their bytes are equal. For correctness-sensitive checks such as validating a copied file, choose shallow=False.
Output
Ready to run.
Understand the shallow shortcut
Two files can have the same type, byte length, and modification time while containing different bytes. When those stat fields match, shallow=True may report a match without reading both contents. This behavior is useful for quick comparisons, but do not treat it as a content-integrity guarantee. Use controlled metadata in examples and shallow=False where the bytes must be checked.
Output
Ready to run.
Compare a known set of names
filecmp.cmpfiles(left_dir, right_dir, common, shallow=False) compares the listed relative names in two directories. It returns three lists in this order: matches, mismatches, and errors. A missing or unreadable file belongs in errors; it is not the same as a readable file whose contents differ. Names in common may include subdirectory components.
Output
Ready to run.
Inspect two directory trees with dircmp
filecmp.dircmp(left, right, shallow=False) builds a directory-comparison object. Its common, left_only, and right_only attributes describe names; common_files and common_dirs separate shared entries by kind; same_files and diff_files describe shared files that compare equal or different; funny_files and common_funny identify entries that could not be compared normally or have mismatched types. These attributes are computed lazily, so access only what the report needs.
Output
Ready to run.
Follow directory comparisons into subdirectories
For shared subdirectories, dircmp.subdirs maps each directory name to another dircmp object. Inspect those children recursively when you need a tree-wide comparison. A directory comparison is a snapshot of filesystem state, not a synchronization plan: it does not copy, remove, or repair anything. If you act on its results later, remember that files may change between comparison and action.
Output
Ready to run.
Use the comparison cache responsibly
filecmp caches prior comparisons to avoid repeating work. Cache entries are invalidated when the recorded stat information changes, but a quick rewrite can occur within the timestamp resolution of a filesystem. If a comparison immediately follows a write and the result must reflect the newest bytes, call filecmp.clear_cache() before comparing. Clearing the cache does not change files; it only discards saved comparison results.
Output
Ready to run.
Ignore expected generated names
Pass ignore to dircmp when known generated entries should not affect the comparison, such as an application cache or a local build artifact. Ignoring a name is a reporting/comparison choice, not a deletion: filecmp does not modify either directory. Keep the ignore list narrow so unexpected differences remain visible.
Output
Ready to run.
Choose the right comparison tool
Use filecmp when you need Boolean file comparisons or structural directory reports. Use difflib when people need to see which text lines differ. Use a cryptographic hash when the task requires a portable content fingerprint for data that may cross systems; a hash is still not a substitute for a threat model or authenticated provenance. For copying or updating files, use shutil or explicit application logic after deciding what a difference means.
Force content comparison
Complete the call so the comparison checks file contents rather than trusting matching stat signatures.
import filecmp
from pathlib import Path
from tempfile import TemporaryDirectory
with TemporaryDirectory() as temporary:
first = Path(temporary) / "one.txt"
second = Path(temporary) / "two.txt"
first.write_text("same", encoding="utf-8")
second.write_text("same", encoding="utf-8")
assert filecmp.cmp(first, second, ) is True
print("content checked")
Separate differences from comparison errors
Choose the API that reports names as matches, mismatches, and errors.
import filecmp
from pathlib import Path
from tempfile import TemporaryDirectory
with TemporaryDirectory() as temporary:
left = Path(temporary) / "left"
right = Path(temporary) / "right"
left.mkdir()
right.mkdir()
(left / "a.txt").write_text("A", encoding="utf-8")
(right / "a.txt").write_text("B", encoding="utf-8")
matches, mismatches, errors = filecmp.(left, right, ["a.txt"], shallow=False)
assert matches == [] and mismatches == ["a.txt"] and errors == []
print(mismatches)
Read directory comparison results
Complete the attribute that lists common files whose contents differ.
import filecmp
from pathlib import Path
from tempfile import TemporaryDirectory
with TemporaryDirectory() as temporary:
left = Path(temporary) / "left"
right = Path(temporary) / "right"
left.mkdir(); right.mkdir()
(left / "x.txt").write_text("left", encoding="utf-8")
(right / "x.txt").write_text("right", encoding="utf-8")
result = filecmp.dircmp(left, right, shallow=False)
assert result. == ["x.txt"]
print(result.diff_files)
Inspect child directory comparisons
Complete the expression to get the comparison object for a shared child directory named data.
import filecmp
from pathlib import Path
from tempfile import TemporaryDirectory
with TemporaryDirectory() as temporary:
left = Path(temporary) / "left" / "data"
right = Path(temporary) / "right" / "data"
left.mkdir(parents=True); right.mkdir(parents=True)
(left / "item.txt").write_text("left", encoding="utf-8")
(right / "item.txt").write_text("right", encoding="utf-8")
comparison = filecmp.dircmp(left.parent, right.parent, shallow=False)
nested = comparison.["data"]
assert nested.diff_files == ["item.txt"]
print(nested.diff_files)
Refresh cached comparisons after a rapid change
Choose the function that removes cached results before the next comparison.
import filecmp
assert callable(filecmp.clear_cache)
print("cache reset available")
Choose between an equality check and a line diff
Choose the standard-library module designed to show differences between text sequences.
from import unified_diff
assert callable(unified_diff)
print("text diff helper imported")
Thanks for your feedback.
Mark this learning resource complete to track your learning progress.