Linux Basics: Compare Sorted Lists with GNU comm
When you have two inventories, the useful questions are often what they share and what appears in only one. GNU comm answers those questions for sorted text files. Unlike a line-by-line edit diff, it treats the input as ordered lists to compare.
These examples were tested on Linux with GNU Bash 5.2.37 and GNU coreutils 9.7. They use invented service names, not a real deployment. Each script sets LC_ALL=C so sorting and comparison use the same predictable ordering.
Prepare a scratch directory
You need Bash, GNU comm and sort, a text editor, and the mktemp, rm, and rmdir utilities. Keep using the same terminal. Save each named source file here, then run it once in order. No administrator access is needed.
start_dir="$PWD"
work_dir="$(mktemp -d)" || exit 1
cd -- "$work_dir" || exit 11. Understand the three output columns
Save this as comm-three-columns.sh:
#!/usr/bin/env bash
set -eu
export LC_ALL=C
printf '%s\n' apple banana fig > left.txt
printf '%s\n' banana cherry fig > right.txt
printf 'Only left|Only right|Both\n'
comm --check-order --output-delimiter='|' left.txt right.txtRun it:
bash comm-three-columns.shOutput:
Only left|Only right|Both
apple
||banana
|cherry
||figThe first column is unique to the first file, the second is unique to the second file, and the third is shared. The two leading separators before banana place it in the shared column. Trailing empty columns are not padded out, so apple has no trailing separators.
GNU comm normally separates columns with tabs. The custom | delimiter makes column positions visible here; it does not turn the result into a general-purpose CSV file. The GNU comm manual explains column suppression and ordering requirements.
2. Produce missing and unexpected inventories
Save this as comm-inventory-difference.sh:
#!/usr/bin/env bash
set -euo pipefail
export LC_ALL=C
printf '%s\n' web api cache api | sort -u > expected.txt
printf '%s\n' worker web api | sort -u > actual.txt
printf 'Missing:\n'
comm --check-order -23 expected.txt actual.txt
printf 'Unexpected:\n'
comm --check-order -13 expected.txt actual.txt
printf 'Shared:\n'
comm --check-order -12 expected.txt actual.txtRun it:
bash comm-inventory-difference.shOutput:
Missing:
cache
Unexpected:
worker
Shared:
api
webThe numbers name columns to hide, not columns to select. -23 hides columns two and three, leaving entries found only in the expected list. -13 leaves entries unique to the actual list; -12 leaves the intersection.
sort -u both orders the fixtures and removes the repeated api. This gives set-like membership semantics: being listed twice does not create a second required service. Do not remove duplicates if multiplicity carries meaning in your data; that calls for a different comparison model.
This report only identifies differences. It does not start, stop, create, or delete anything. Review a real inventory’s meaning before using its differences to drive actions.
3. Reject unsorted input and discard partial results
Save this as comm-check-order.sh:
#!/usr/bin/env bash
set -eu
export LC_ALL=C
printf '%s\n' beta alpha > unsorted.txt
printf '%s\n' alpha beta > ordered.txt
if comm --check-order -12 unsorted.txt ordered.txt \
> candidate.txt 2> order-error.txt; then
printf 'Unexpected success\n' >&2
exit 1
else
status=$?
test -s order-error.txt
rm -- candidate.txt
printf 'Comparison rejected: status %s\n' "$status"
printf 'Candidate output discarded\n'
fiRun it:
bash comm-check-order.shOutput:
Comparison rejected: status 1
Candidate output discardedThe first fixture deliberately violates the required order. --check-order rejects it, and the conditional handles that expected failure. The error text is retained separately while the candidate result is removed. Do not use partial comparison output after an ordering error.
The script finishes successfully because rejection is the expected outcome of this test. For real input, fix the preparation step rather than using --nocheck-order to hide the problem.
Keep in mind
Both input files must use the same ordering as the comparison. Numeric sorting is not interchangeable with the lexical ordering used here. Preserve one locale consistently through preparation and comparison.
A successful comm normally returns zero even when the lists differ. Its exit status signals whether comparison succeeded, not whether the lists are equal. Inspect the relevant output if differences should affect a later decision.
These examples assume one complete item per line. An embedded newline would split an item. GNU comm -z supports NUL-delimited records for workflows that need them, but the producer and sorting stages must also use the same record separator.
Clean up
Remove only these practice files, then return to your original directory:
rm -- comm-three-columns.sh comm-inventory-difference.sh comm-check-order.sh \
left.txt right.txt expected.txt actual.txt unsorted.txt ordered.txt order-error.txt
cd -- "$start_dir" || exit 1
rmdir -- "$work_dir"
unset start_dir work_dirAcknowledgements
This post was created with assistance from AI.