查找多个文件之间的共同点

查找多个文件之间的共同点

我有 4 个文件,类似于

       file A
       >TCONS_00000867
       >TCONS_00001442
       >TCONS_00001447
       >TCONS_00001528
       >TCONS_00001529
       >TCONS_00001668
       >TCONS_00001921

       file b
       >TCONS_00001528
       >TCONS_00001529
       >TCONS_00001668
       >TCONS_00001921
       >TCONS_00001922
       >TCONS_00001924

       file c
       >TCONS_00001529
       >TCONS_00001668
       >TCONS_00001921
       >TCONS_00001922
       >TCONS_00001924
       >TCONS_00001956
       >TCONS_00002048

       file d
       >TCONS_00001922
       >TCONS_00001924
       >TCONS_00001956
       >TCONS_00002048

所有文件均包含超过 2000 行,并按第一列排序。

我想找到所有文件中的共同点。我尝试了 awk、grep 和 comm,但不起作用。

答案1

由于文件已经排序:

comm -12 a b |
  comm -12 - c |
  comm -12 - d

comm查找comm文件之间的行。默认情况下comm打印 3 个制表符分隔的列:

  1. 第一个文件特有的行,
  2. 第二个文件特有的行,
  3. 两个文件共有的行。

使用-1, -2,-3选项,我们抑制相应的列。因此报告 和的comm -12 a b公共行。可以用来代替文件名来表示 stdin。ab-

答案2

cat a b c d |sort |uniq -c |sed -n -e 's/^ *4 \(.*\)/\1/p'

答案3

comm如果它需要 3 个以上的文件作为输入,那就太酷了。因为它没有,所以我看不到它的价值,并且建议只使用grep

  • 不需要预先排序的输入
  • 不必抑制输出列
  • 命令就像这样简短而简单comm
grep -f a.txt b.txt | 
  grep -f - c.txt |
  grep -f - d.txt

-f <file>告诉 grep 查找模式,因此在 in中找到的<file>任何行都将作为匹配项输出。这会沿着管道继续下去,因此在AND中找到的行将用作模式来匹配.在管道的末尾,显示的唯一输出是每个文件中出现的行。a.txtb.txta.txtb.txtc.txt

答案4

使用(以前称为 Perl_6)

~$ raku -e 'my %h;  for dir(test => / file \w /) {   \
               %h{$_}++ for .lines.unique };         \
            .put if .value == 4 for %h;'

#OR

~$ raku -e 'my %h; my @a = dir(test => / file \w /);  \
            for @a { %h{$_}++ for .lines.unique };    \
            for %h { .put if .value == @a.elems };'

以上是用 Raku(Perl 编程语言家族的成员)编写的答案。简而言之,Raku 的dir函数用于检查本地目录并提取与文件名匹配的正则表达式。在这里,我们假设文件的命名file后跟一个单词字符,但(比方说)将文件与适当的正则表达式(不是通配模式)\w进行匹配也同样容易。\.txt

对于上面的两个答案,%h声明了一个哈希值。一旦获得文件名(实际上是.IO对象),它们就会通过line明智的使用进行迭代for,每次看到相同的行时都会增加哈希键。

value在第一个答案(最后一个语句)中,如果匹配4,即从所有四个输入文件中返回行。在第二个答案(最后一个语句)中,如果value匹配@a.elems,即输入文件的数量(换句话说,该值是动态设置的),则返回行。

示例输入(注意:文件被命名为fileAfileB等。此外, fileA与 OP 的第一个文件相比,末尾有一个额外的行):

       fileA
       >TCONS_00000867
       >TCONS_00001442
       >TCONS_00001447
       >TCONS_00001528
       >TCONS_00001529
       >TCONS_00001668
       >TCONS_00001921
       >TCONS_00001922

       fileB
       >TCONS_00001528
       >TCONS_00001529
       >TCONS_00001668
       >TCONS_00001921
       >TCONS_00001922
       >TCONS_00001924

       fileC
       >TCONS_00001529
       >TCONS_00001668
       >TCONS_00001921
       >TCONS_00001922
       >TCONS_00001924
       >TCONS_00001956
       >TCONS_00002048

       fileD
       >TCONS_00001922
       >TCONS_00001924
       >TCONS_00001956
       >TCONS_00002048

示例输出(显示所有键/值计数):

~$ raku -e 'my %h; for dir(test => / file \w /) { %h{$_}++ for .lines.unique }; .say for %h.sort: -*.value;'
>TCONS_00001922 => 4
>TCONS_00001668 => 3
>TCONS_00001921 => 3
>TCONS_00001924 => 3
>TCONS_00001529 => 3
>TCONS_00002048 => 2
>TCONS_00001528 => 2
>TCONS_00001956 => 2
>TCONS_00000867 => 1
>TCONS_00001442 => 1
>TCONS_00001447 => 1

示例输出(仅显示.value == 4或 的行.value == @a.elems):

~$ raku -e 'my %h; my @a = dir(test => / file \w /); for @a { %h{$_}++ for .lines.unique }; for %h { .key.put if .value == @a.elems};'
>TCONS_00001922

最后,对于那些喜欢 shell 通配符的人,Raku 也可以做到这一点。关键是记住$*ARGFILES必须转换动态变量才能.handles正确读取输入:

~$ raku -e 'my ($n,%h); for $*ARGFILES.handles -> $fh { $n++; %h{$_}++ for $fh.lines.unique }; for %h { .key.put if .value == $n };' file?
>TCONS_00001922

注意:OP 的测试输入似乎是一个棘手的问题,因为所有四个文件之间没有共同的行!因此,第一个文件 ( fileA) 已被修改以提供阳性对照:>TCONS_00001922

https://stackoverflow.com/a/68774047/7270649
https://docs.raku.org/routine/dir
https://docs.raku.org
https://raku.org

相关内容