![查找多个文件之间的共同点](https://linux22.com/image/5691/%E6%9F%A5%E6%89%BE%E5%A4%9A%E4%B8%AA%E6%96%87%E4%BB%B6%E4%B9%8B%E9%97%B4%E7%9A%84%E5%85%B1%E5%90%8C%E7%82%B9.png)
我有 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
选项,我们抑制相应的列。因此报告 和的comm -12 a b
公共行。可以用来代替文件名来表示 stdin。a
b
-
答案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.txt
b.txt
a.txt
b.txt
c.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
,即输入文件的数量(换句话说,该值是动态设置的),则返回行。
示例输入(注意:文件被命名为fileA
、fileB
等。此外, 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