commdiff is a well-known tool to see what lines are different between files; comm is a less-known tool to see the common lines.
I found comm when I was looking for a way to identify lines that are shared (or not) between files. Specifically, I had two lists: one with all the APIs in my codebase, and another with all the tested APIs. From this I wanted to get a list of untested APIs.
As an aside, my go-to in this situation is to drop in a Ruby one-ish-liner:
ruby -e "puts (File.readlines('all') - File.readlines('tested'))"
In Ruby, Array.- (minus) returns a new array after removing the elements from the right-hand side. IMO, this is pretty nice. Ruby has a wide variety of methods for combining arrays (+ to merge, - for difference, & for intersection, | for union). The only caveat is that this is trickier if you haven’t written the lines to a file. That’s pretty common in pipelines and was, in fact, exactly the situation in which I found myself.
Let’s say we have these two lists: mammals.txt and pets.txt:
$ cat mammals.txt
cat
dog
lion
rabbit
whale
$ cat pets.txt
cat
dog
fish
rabbit
snake
We can compare the files with comm:
$ comm mammals.txt pets.txt
cat
dog
fish
lion
rabbit
snake
whale
The first time I saw this output I was pretty confused. Is this working? It kind of looks like I just dumped all the lines with weird indentation? comm produces output in three columns:
Here’s a labeled version:
$ comm mammals.txt pets.txt
just | just |
mammals.txt | pets.txt | both
--------------+------------+-------
| | cat
| | dog
| fish |
lion | |
| | rabbit
| snake |
whale | |
You can use -1 to hide the first column, -2 to hide the second column, and, yes, -3 to hide the third column. In this case, if you want just animals that are mammals and pets: comm -12 mammals.txt pets.txt:
$ comm -12 mammals.txt pets.txt
cat
dog
rabbit
If you want pets that aren’t mammals, that would be comm -13 mammals.txt pets.txt.
To get back to my original use case, if I want a list of untested APIs, you write:
comm -23 all_apis.txt tested_apis.txt
comm works with process substitution so you can also write:
comm -23 <(command to find all APIs) <(command to find tested APIs)
One caveat: the input lines need to be sorted, so you may need to add | sort.