comm Command Explained: Comparing Two Sorted Files
comm compares two sorted files and answers a question diff answers awkwardly: which lines are in one, which in the other, and which in both.
The three columns
comm file1.txt file2.txt
Output has three tab-separated columns:
- Lines only in
file1 - Lines only in
file2 - Lines in both
Suppress columns with digits, and note that the digit names the column you are removing:
comm -12 file1 file2 # only lines in both, the intersection
comm -23 file1 file2 # only in file1, the difference
comm -13 file1 file2 # only in file2
comm -3 file1 file2 # everything except the common lines
-12 reads as “hide 1 and 2, show 3,” which is backwards from intuition until it sticks.
Sorting is mandatory
comm makes a single pass and never backtracks. Unsorted input produces output that is silently wrong.
sort file1.txt -o file1.txt
sort file2.txt -o file2.txt
comm -12 file1.txt file2.txt
Use the same collation on both. Locale differences change sort order, and the safest habit for anything scripted is:
LC_ALL=C sort file1 > a
LC_ALL=C sort file2 > b
LC_ALL=C comm -12 a b
Comparing command output
This is how it is usually used, avoiding temporary files entirely:
# Packages installed here but not there
comm -23 \
<(ssh host1 'dpkg -l | awk "/^ii/{print \$2}"' | sort) \
<(ssh host2 'dpkg -l | awk "/^ii/{print \$2}"' | sort)
# Users on one system and not the other
comm -13 <(cut -d: -f1 /etc/passwd | sort) <(sort expected-users.txt)
# Which files exist in the backup but not the source
comm -13 <(cd /srv/data && find . -type f | sort) \
<(cd /backup/data && find . -type f | sort)
That last one answers “what did the backup keep that the source no longer has,” which is a genuinely useful question after an accidental deletion.
Process substitution is covered in our heredocs and process substitution guide.
comm or diff
comm treats files as sets. It answers membership questions and is fast, because it is one linear pass.
diff treats files as sequences. It answers “how do I turn this into that” and understands context, moves, and ordering.
For a list of package names, usernames, IP addresses, or file paths, comm is the right tool and the output is far easier to read. For source code or configuration where order matters, diff is.
Duplicates
comm assumes each line appears once. Repeated identical lines produce results that are technically correct and rarely what you meant.
sort -u file1 > a # -u removes duplicates while sorting
sort -u file2 > b
comm -12 a b
Adding -u to the sort is usually the right default when doing set comparison.
Frequently Asked Questions
What does comm do?
It compares two sorted files line by line and outputs three columns: lines unique to the first file, lines unique to the second, and lines present in both. Suppressing columns turns it into a set difference or intersection tool.
Why does comm say my input is not sorted?
Because it requires sorted input and compares in a single pass. Sort both files first with the same collation, and set LC_ALL=C on both the sort and the comm if the locale is producing an unexpected order.
How do I get only the lines in both files?
Suppress columns one and two with -12, leaving only the third column, which is the intersection. The digits refer to the columns you want hidden, which reads backwards until you get used to it.
What is the difference between comm and diff?
diff shows how to transform one file into the other and cares about order and context. comm treats the files as sets of lines and tells you membership. For questions about which items are present, comm is clearer and much faster.
Can comm compare unsorted data?
Not correctly. It will produce output and the output will be wrong, because it assumes sorted input and never backtracks. Sorting is not optional.
How do I compare command output rather than files?
Use process substitution to present each command as a file, sorting each one inside the substitution. This avoids writing temporary files and is the most common way comm is used in practice.