使用 grep 查找不在两个文件中的文本

使用 grep 查找不在两个文件中的文本

我想搜索两个文本文件并将每个文件中仅在其中一个文件中的行打印到一个文件中。

例如,LIST-1.txt 包含:

apples
dogs
paintings
mom
dad
don

LIST-2.txt 包含

apples
don
dad
mom
cats

我希望输出为(以任何顺序):

dogs
paintings
cats

我已经尝试过这个:

cat list-1.txt | while read line || [[ -n $line ]];
do
   grep -v $line list-2.txt
done

有什么建议么?

答案1

您的循环的问题是,在每次迭代中,您都会得到第二个文件中与第一个文件中当前行不同的所有行

循环的一个变体是以下两个命令的输出的串联:

grep -v -xF -f LIST-1.txt LIST-2.txt
grep -v -xF -f LIST-2.txt LIST-1.txt

第一个grep将获取与LIST-2.txt中的任何行不完全对应的所有行LIST-1.txt,而第二个则grep对两个文件进行相同的操作。这实际上会给您在问题中提到的结果。 (我看到杰夫已经提到过这一点,所以如果你喜欢这种方法,请投票给他的答案,而不是我的。)

它确实需要将其中一个文件读入内存(作为查询字符串),并且可能会被认为有点不优雅。我也没有真正考虑过在什么情况下它可能无法提供正确的结果。

就我个人而言,我会选择

$ join -v 1 -v 2 <( sort LIST-1.txt ) <( sort LIST-2.txt )
cats
dogs
paintings

这将在文件之间执行关系 JOIN 操作。通常,这将返回两个文件中都存在的条目(内部联接),但在这里我们要求-v 1 -v 2查看所有条目在任一文件中匹配。

join实用程序需要排序的输入(一次只能在内存中保存每个文件的一行),这就是为什么我们对这两个文件进行排序并join通过单独的进程替换提供它们。

在没有进程替换的 shell 中,您可能需要在调用之前创建文件的排序副本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

答案2

因为刚刚提到过,但从未明确解释过:GNUcomm解决方案

comm --output-delimiter '' -3 <(sort file1) <(sort file2)

-3抑制出现在两个文件中的行,并且分隔符规范将仅左对齐结果。但是,需要对文件进行排序才能comm正常工作。

答案3

如果您不关心重新排序的结果,一种方法可能是:

  1. sort两个文件放在一起
  2. 要求uniq计算相邻重复项的数量
  3. 要求awk只打印不重复的行
  4. 要求cut删除uniq的计数
sort list-1.txt list-2.txt | uniq -c | awk '$1 == 1 { print}' | cut -c9-

要强制 grep 执行此操作,您可以在两个方向上运行整行、固定文本、基于文件的排除:

{ grep -vxF -f list-1.txt list-2.txt; grep -vxF -f list-2.txt list-1.txt; }

这要求grep第二个文件中第一个文件中不存在的行,我们在其中反转第二个文件的文件名grep

答案4

如果您的输入文件没有单独的重复条目,您可以使用:

sort list[12] |uniq -u

或使用awk作为替代:

awk '{ seen[$0]++ } END{ for (x in seen) if (seen[x]==1) print x }' list[12]

相关内容