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

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

我在 Linux 机器上有 6 个 .txt 文件,每个文件大约有 1700 行。我想查看这 6 个文件中的 6 个中常见的行,然后分别是 6 个文件中的 5 个、6 个文件中的 4 个、6 个文件中的 3 个以及 6 个文件中的最后一个。我尝试使用以下代码:

comm -12 2.txt 3.txt | comm -12 - 4.txt | comm -12 - 5.txt | comm -12 - 6.txt | comm -12 - 7.txt

但我注意到结果不完整且不兼容。然后我做了一些研究并尝试了以下代码:

awk 'NR==FNR { lines[$0]=1; next } $0 in lines' 2.txt 3.txt 4.txt 5.txt 6.txt 7.txt

但这也不起作用。

我是 Ubuntu 新手,所以我只知道非常基本的东西。如果你能帮助我,我将不胜感激。提前致谢。

编辑1:在@terdon的建议下,我还尝试了以下代码:

sort file1 file2 file3 file4 file5 file6 | uniq -c

它给了我这样的输出:

3 VAL455-Main VAL451-Main 
1 VAL455-Main VAL451-Main 
3 VAL463-Main ALA459-Main 
1 VAL463-Main ALA459-Main 
3 VAL468-Main LEU466-Main 
1 VAL468-Main LEU466-Main 
3 VAL468-Main LYS464-Main 

我们来看看“VAL468-Main LEU466-Main”。正如您所看到的,输出中有两个不同的结果。它表示该行同时存在于 3 个文件中和仅存在于 1 个文件中。但是,该行出现在所有文件中。

所以我们还没有找到解决办法。

编辑2:感谢@FelixJN,我们找到了问题的根源。如果你也有类似的情况,可以看看他的回答。 :)

答案1

awk您一起可以执行以下操作:

#skip if multiple appearance in one file
{if ( seenin[$0] ~ FILENAME ) {next}}
#add filename to list of files the line has been seen in, increase seen counter
{seenin[$0]=seenin[$0]" "FILENAME ; nseen[$0]++}

#print
END {for (line in nseen) { if (nseen[line]>1) {
   printf "%s \"%s\" %s %d %s %s\n",
     "line",line,"seen in",nseen[line],"files:",seenin[line]} } }

限制:内存,因为所有行都保存在 RAM 中。

如果您想按出现次数排序,则必须相应地调整打印命令,例如按 的值排序nseen。这gawk很简单:在END块中的 -loop 之前添加以下内容for

PROCINFO["sorted_in"]="@val_num_desc"

输入文件:

$ cat file1
a
a
b
b
c
d
e

$ cat file2
c
c
x
z
e
y
z
f

$ cat file3
f
i
a
c
z
i
k

输出(具有gawk数组遍历功能PROCINFO

$awk -f compare_lines_multifiles.awk file1 file2 file3
line "c" seen in 3 files:  file1 file2 file3
line "z" seen in 2 files:  file2 file3
line "a" seen in 2 files:  file1 file3
line "e" seen in 2 files:  file1 file2
line "f" seen in 2 files:  file2 file3

编辑:

您提供的文件具有 MSDOS 格式。要么通过转换它们

 dos2unix file1.txt file2.txt ....

或调整 中的记录分隔符awk。作为代码中的第一个条目添加以下内容:

 BEGIN { RS="\r\n" }

编辑2:您的文件有不规则的分隔符。问题是,a<tab>ba<tab>b<tab>被视为不同的行,而您可能认为它们是相同的。

对于每个文件有两个感兴趣字段的特殊情况,您宁愿比较这两个字段的内容,而不是整行。还考虑 MSDOS 格式:

BEGIN { RS="\r\n" }
#skip if multiple appearance in one file
{if ( seenin[$1"\t"$2] ~ FILENAME ) {next}}
#add filename to list of files the line has been seen in, increase seen counter
{seenin[$1"\t"$2]=seenin[$1"\t"$2]" "FILENAME ; nseen[$1"\t"$2]++}

#print
END {for (line in nseen) { if (nseen[line]>1) {
   printf "%s \"%s\" %s %d %s %s\n",
     "line",line,"seen in",nseen[line],"files:",seenin[line]} } }

最终所有六个文件都有更多的重叠。它专注于带有制表符分隔符的两个字段,并打印一行的输出。

答案2

我建议采用不同的方法。只需将它们全部遍历sortuniq -c计算每行被看到的次数:

sort 2.txt 3.txt 4.txt 5.txt 6.txt 7.txt | uniq -c

这将打印每行一次,但也会打印该行被看到的次数。例如,如果我有这三个文件:

$ cat file1 
dog
cat
bird

$ cat file2
fly
bird
moose

$ cat file3
bird
dog
flea

我会得到这个输出:

$ sort file1 file2 file3 | uniq -c
      3 bird
      1 cat
      2 dog
      1 flea
      1 fly
      1 moose
    

因此,如果您想按照发现的次数来分隔行,您可以执行以下操作来仅查看所有 3 个(或 6 个,在您的情况下)文件中出现的行:

$ sort file1 file2 file3 | uniq -c | awk '$1==3'
  3 bird
$ sort file1 file2 file3 | uniq -c | awk '$1==2'
      2 dog
$ sort file1 file2 file3 | uniq -c | awk '$1==1'
      1 cat
      1 flea
      1 fly
      1 moose

答案3

您的第一次尝试是正确的方法:

comm -12 2.txt 3.txt | comm -12 - 4.txt | comm -12 - 5.txt | comm -12 - 6.txt | comm -12 - 7.txt

这就像流一样并行地完成工作。原则上,您可以通过这种方式处理具有数百万行的文件。

你遇到的问题通讯(1) 似乎是由输入问题引起的,即空格和行结尾。如果您先清理这些内容,您可能会发现原来的方法既快速又方便。

这里有一个例子来证明这一点。查找可被素数组整除的数字:

$ for D in 2 3 5 7 11 13 
> do seq 1 1000 | 
> awk -v D=$D '$0 % D == 0 { print $0 }' | 
> sort > $D
> done

$ comm -12 2 3 | comm -12 - 5 | comm -12 - 7 
210
420
630
840

事实证明,1 到 1000 之间没有数字可以被 2、3、5、7 和 11 整除。

答案4

# spits a random three char (a..e)
# string each time it's called
rx() {
  < /dev/urandom \
  tr -cd 'a-e'   |
  fold -w3       |
  head -n 1      \
  ;
}

# modify this section
numFiles=6
numLines=70
commIn=5

# generate random files
for j in $(seq "$numFiles"); do
  for i in $(seq "$numLines"); do
    rx
  done > $j.txt
done

# uniquify each file
# then sort the combined pool
# count n tabulate uniques from this pool
for i in $(seq "$numFiles"); do
  sort -u < "$i.txt"
done                  |
sort | uniq -c        |
sort -b -k1,1nr -k2,2 |
awk -v n="$commIn" '$1+0==n'   ;

本次运行结果:

      5 acc
      5 bcd
      5 dac
      5 eea

相关内容