关于shell:Unix命令查找两个文件中常见的行

Unix command to find lines common in two files

我确定我曾经发现一个unix命令可以打印两个或多个文件中的公共行,有人知道它的名字吗? 它比diff简单得多。


您正在寻找的命令是comm。例如:-

1
comm -12 1.sorted.txt 2.sorted.txt

这里:

-1:禁止第1列(1.sorted.txt唯一的行)

-2:抑制第2列(2.sorted.txt唯一的行)


要轻松将comm命令应用于未排序的文件,请使用Bash的进程替换:

1
2
3
4
5
6
7
8
9
10
11
$ bash --version
GNU bash, version 3.2.51(1)-release
Copyright (C) 2007 Free Software Foundation, Inc.
$ cat > abc
123
567
132
$ cat > def
132
777
321

所以文件abc和def有一条共同的行,一行是"132"。
在未排序的文件上使用comm:

1
2
3
4
5
6
7
8
9
$ comm abc def
123
    132
567
132
    777
    321
$ comm -12 abc def # No output! The common line is not found
$

最后一行没有产生输出,没有发现公共线。

现在使用comm对已排序的文件,使用进程替换对文件进行排序:

1
2
3
4
5
6
7
8
$ comm <( sort abc ) <( sort def )
123
            132
    321
567
    777
$ comm -12 <( sort abc ) <( sort def )
132

现在我们得到了132线!


为了补充Perl单线程,这里是awk等价物:

1
awk 'NR==FNR{arr[$0];next} $0 in arr' file1 file2

这将读取从file1到数组arr[]的所有行,然后检查file2中的每一行是否已存在于数组中(即file1)。找到的行将按照它们在file2中出现的顺序打印。
请注意,比较in arr使用file2中的整行作为数组的索引,因此它仅报告整行的完全匹配。


也许你的意思是comm

Compare sorted files FILE1 and FILE2 line by line.

With no options, produce three-column output. Column one
contains lines unique to FILE1, column
two contains lines unique to
FILE2, and column three contains lines common to both files.

查找这些信息的秘诀是信息页面。对于GNU程序,它们比人工页面更详细。尝试info coreutils,它会列出所有小的有用工具。


1
grep -v -f 1.txt 2.txt > 3.txt

给你两个文件的差异(2.txt中的内容,而不是1.txt中的内容),你可以很容易地做到

1
grep -f 1.txt 2.txt > 3.txt

收集所有常见的行,这应该为您的问题提供简单的解决方案。如果您已经排序了文件,那么您应该使用comm。问候!


如果这两个文件尚未排序,您可以使用:

1
comm -12 <(sort a.txt) <(sort b.txt)

并且它将工作,在执行comm -12 a.txt b.txt时避免错误消息comm: file 2 is not in sorted order


1
perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/'  file1 file2


1
awk 'NR==FNR{a[$1]++;next} a[$1] ' file1 file2

在Linux的有限版本上(就像我正在研究的QNAP(nas)):

  • comm不存在
  • grep -f file1 file2可能会导致一些问题,如@ChristopherSchultz所说,使用grep -F -f file1 file2非常慢(超过5分钟 - 没有完成 - 超过2-3秒,使用下面的方法,文件超过20MB)

所以这就是我所做的:

1
2
3
4
5
sort file1 > file1.sorted
sort file2 > file2.sorted

diff file1.sorted file2.sorted | grep"<" | sed 's/^< *//' > files.diff
diff file1.sorted files.diff | grep"<" | sed 's/^< *//' > files.same.sorted

如果files.same.sorted的顺序与原始顺序相同,则将此行添加到与file1相同的顺序:

1
awk 'FNR==NR {a[$0]=$0; next}; $0 in a {print a[$0]}' files.same.sorted file1 > files.same

或者,与file2相同的顺序:

1
awk 'FNR==NR {a[$0]=$0; next}; $0 in a {print a[$0]}' files.same.sorted file2 > files.same

仅供参考,如果有人仍在查看如何为多个文件执行此操作,请参阅查找多个文件中的匹配行的链接答案。

结合这两个答案(ans1和ans2),我认为你可以得到你需要的结果,而无需对文件进行排序:

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
ans="matching_lines"

for file1 in *
do
    for file2 in *
        do
            if  ["$file1" !="$ans" ] && ["$file2" !="$ans" ] && ["$file1" !="$file2" ] ; then
                echo"Comparing: $file1 $file2 ...">> $ans
                perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/' $file1 $file2 >> $ans
            fi
         done
done

只需保存它,赋予它执行权限(chmod +x compareFiles.sh)并运行它。它将获取当前工作目录中存在的所有文件,并将在"matching_lines"文件中进行全对比比较结果。

有待改进的地方:

  • 跳过目录
  • 避免两次比较所有文件(file1 vs file2和file2 vs file1)。
  • 也许在匹配字符串旁边添加行号

1
2
3
4
5
6
7
8
9
10
11
rm file3.txt

cat file1.out | while read line1
do
        cat file2.out | while read line2
        do
                if [[ $line1 == $line2 ]]; then
                        echo $line1 >>file3.out
                fi
        done
done

这应该做到这一点。