How to Compare Two Lists in Bash
Bash doesn't need a scripting language for this - a handful of standard Unix tools already do exactly what you want, as long as you know their sorting requirements and matching rules. Here are the four commands worth knowing, ordered by how often you'll reach for each.
Quick answer
comm -23 <(sort a.txt) <(sort b.txt) prints lines only in a.txt, but both files must be sorted first - comm compares line by line and produces wrong results on unsorted input without any warning. grep -Fxvf b.txt a.txt does the same job without requiring sorted input, using exact fixed-string whole-line matching. Use join instead of comm/grep when you need to match on one column rather than the whole line.
Need the same thing in another language?
See the full reference for Python, JavaScript, SQL, Java, PowerShell, and more.
comm - the standard tool, if you sort first
comm -23 <(sort a.txt) <(sort b.txt)
comm prints three tab-indented columns by default: lines only in the first file, lines only in the second, and lines in both. The numbers after - suppress columns - -23 hides columns 2 and 3, leaving only lines unique to a.txt. The <(sort ...) process substitution sorts each file on the fly without creating a temp file - this step is mandatory, since comm assumes sorted input and gives silently wrong results otherwise.
grep -Fxvf - no sorting required
grep -Fxvf b.txt a.txt
This reads patterns from b.txt (-f b.txt) as literal strings rather than regex (-F), requires a full-line exact match (-x), and inverts the result to show non-matches (-v). The result: every line of a.txt that doesn't appear anywhere in b.txt, in a.txt's original order, with no sorting step needed.
diff - when you need context, not just membership
diff a.txt b.txt
diff answers a different question than comm and grep: it shows how to transform one file into the other, matching by position and context, not just by which lines exist in each file. It's the right tool when line order and grouping matter (like comparing two versions of a config file); it's the wrong tool for "which items exist in A but not B" on lists where order doesn't matter, since a single reordering will show up as a wall of unrelated-looking changes.
join - comparing by a column, not the whole line
join -v 1 <(sort -k1,1 a.txt) <(sort -k1,1 b.txt)
When each line has multiple fields and you want to match on just one of them (like an ID column) rather than the entire line, join does what comm can't. Both inputs need to be sorted on the join key first (-k1,1 sorts on the first field), and -v 1 prints unmatched lines from the first file only.
The usual culprit when lines "should" match but don't
Trailing whitespace, a stray carriage return left over from a file created on Windows (CRLF line endings instead of Unix LF), or a non-breaking space pasted from a web page are the most common reasons two visually identical lines fail to match in any of these tools. cat -A file.txt reveals line endings and tabs directly in the terminal; the Invisible Character Detector gives a clearer visual breakdown if you'd rather not parse terminal escape codes by eye.
Just need the answer, not the code? Paste both lists.
Open Compare 2 Lists →Frequently Asked Questions
What is the comm command and what do its columns mean?
comm takes two sorted files and prints three tab-separated columns: lines only in the first file, lines only in the second, and lines common to both. comm -23 a.txt b.txt suppresses columns 2 and 3, leaving only lines unique to a.txt.Why does comm require sorted input?
sort first, typically via process substitution: comm -23 <(sort a.txt) <(sort b.txt).What's the simplest way to compare two lists in Bash without sorting them first?
grep -Fxvf b.txt a.txt - this prints every line of a.txt that does NOT appear anywhere in b.txt, using fixed-string (-F) exact whole-line (-x) matching, and doesn't require either file to be sorted.What do the grep flags -Fxvf actually mean?
-F treats patterns as literal fixed strings rather than regex, -x requires the whole line to match exactly rather than matching a substring, -v inverts the match to print non-matching lines, and -f b.txt reads the list of patterns to match from the file b.txt instead of the command line.How do I find lines common to both files (intersection) in Bash?
comm -12 <(sort a.txt) <(sort b.txt) suppresses the unique-to-each columns and leaves only the common lines. Without sorting: grep -Fxf b.txt a.txt (same flags as the difference version, minus -v).Why do two lines that look identical not match in comm or grep?
cat -A file.txt to reveal hidden characters, or use the Invisible Character Detector for a visual view of exactly which characters are present.How do I compare two files by a specific column, not the whole line?
join -v 1 <(sort -k1,1 a.txt) <(sort -k1,1 b.txt) finds lines from a.txt whose first field has no match in b.txt. Both files need to be sorted on the join key, and the field delimiter defaults to whitespace unless you pass -t.