如何跨多个文件提取公共行?

how to extract common lines across multiple file?

我有 15 个不同的文件,我想要一个新文件,所有文件中只包含公共行。例如:

File1:

id1
id2
id3

file2:

id2
id3
id4

file3:
id10
id2
id3

file4

id100
id45
id3
id2

I need the output be like:

newfile:

id2 
id3

我知道这个命令适用于每一对文件:

grep -w -f file1 file2 > 输出

但我需要一个命令来处理 2 个以上的文件。

有什么建议吗?

使用 grep

同一个技巧可以多次使用:

$ grep -w -f file1 file2 | grep -w -f file3 | grep -w -f file4
id2
id3

顺便说一下,如果您正在寻找精确匹配,而不是 正则表达式 匹配,使用 -F 标志会更好更快:

$ grep -wFf file1 file2 | grep -wFf file3 | grep -wFf file4
id2
id3

使用 awk

$ awk 'FNR==1{nfiles++; delete fseen} !([=12=] in fseen){fseen[[=12=]]++; seen[[=12=]]++} END{for (key in seen) if (seen[key]==nfiles) print key}' file1 file2 file3 file4
id3
id2
  • FNR==1{nfiles++; delete fseen}

    每次我们开始读取新文件时,我们都会做两件事:(1) 增加文件计数器,nfiles。和 (2) 删除数组 fseen.

  • !([=17=] in fseen){fseen[[=17=]]; seen[[=17=]]++}

    如果当前行不是 fseen 中的键,则将其添加到 fseen 并增加此行在 seen 中的计数。

  • END{for (key in seen) if (seen[key]==nfiles) print key}

    读完最后一个文件的最后一行后,我们查看 seen 中的每个键。如果该键的计数等于我们已读取的文件数,nfiles,那么我们打印该键。

Perl 来拯救:

perl -lne 'BEGIN { $count = @ARGV }
           $h{$_}{$ARGV} = 1;
           }{
           print $_ for grep $count == keys %{ $h{$_} }, keys %h
           ' file* > newfile
  • -n逐行读取输入文件
  • -lprint
  • 添加换行符
  • @ARGV 数组包含输入文件名,在 BEGIN 将其分配给 $count 只是计算它们
  • $ARGV包含当前输入文件的名称
  • $_ 包含从文件中读取的当前行。
  • %h 散列包含 id 作为键,每个键包含一个散列引用,文件名包含 id 作为键
  • }{ 是 "Eskimo greeting" 运算符,它引入了一旦输入耗尽就运行的代码
  • 我们只输出文件数等于所有文件数的id。它适用于任意数量的文件。
     grep -hxf file1 file2 file3 file4 |sort -u
     id2
     id3

     # For storing it to any file, 
     grep -hxf file1 file2 file3 file4 |sort -u > output.txt

zet 命令提供输入文件之间的集合操作。使用 intersect 选项获取所有输入文件的公共行。输入内容不必排序。输出顺序将与输入行的顺序相同。

$ zet intersect file1 file2 file3 file4
id2
id3

这里有一些相关的细节from the notes section:

  • 每个输出行只出现一次,因为我们将文件视为集合,将行视为它们的元素。
  • Zet 将整个文件读入内存。它的内存使用量大致与其最大参数的文件大小加上(最终)输出的大小成正比。