The issue with your loop is that in each iteration, you will get all lines in the second file that is not the same as the current line in the first file.
A variation of your loop is the concatenation of the output of the following two commands:
grep -v -xF -f LIST-1.txt LIST-2.txt
grep -v -xF -f LIST-2.txt LIST-1.txt
The first grep
will get all lines from LIST-2.txt
that does not correspond exactly to any line in LIST-1.txt
, while the second grep
does the same thing with the two files reversed. This would actually give you the result that you mention in the question. (I see that Jeff already mentioned this, so if you like this approach, go upvote his answer, not mine.)
It does require reading in one of the files into memory (as query strings), and would possible be considered slightly inelegant. I also haven't really thought about under what circumstances it may fail to provide correct result.
Personally, I would go with
$ join -v 1 -v 2 <( sort LIST-1.txt ) <( sort LIST-2.txt )
cats
dogs
paintings
This performs a relational JOIN operation between the files. Normally, this would return the entries that exists in both files (an inner join), but here we ask with -v 1 -v 2
to see all entries that don't match up in either file.
The join
utility requires sorted input (to be able to only hold one line from each file in memory at a time), which is why we sort both files and provide them to join
via individual process substitutions.
In shells that do not have process substitutions, you may want to create sorted copies of the files before calling join
:
sort -o LIST-1.txt.sorted LIST-1.txt &&
sort -o LIST-2.txt.sorted LIST-2.txt &&
join -v 1 -v 2 LIST-[12].txt.sorted
rm -f LIST-[12].txt.sorted