AWK:在任意列数(单列文件或列矩阵)中查找公共元素

时间:2016-08-02 11:42:23

标签: awk

问题

我有几个文件,每一列,我想将它们彼此进行比较,以找出所有文件中包含的元素。或者 - 如果它更容易 - 我可以制作一个列矩阵。

问题

如何在多列中找到公共元素。

请求

我不是awk的专家(显然)。因此,非常感谢对代码的详细解释。

其他

@ joepvd制作了一些类似的代码... https://unix.stackexchange.com/questions/216511/comparing-the-first-column-of-two-files-and-printing-the-entire-row-of-the-secon/216515#216515?newreg=f4fd3a8743aa4210863f2ef527d0838b

3 个答案:

答案 0 :(得分:2)

  

查找所有文件中包含的元素

当你猜到时,

awk是你的朋友。请使用以下步骤

#Store the files in an array. Assuming all files in one place
filelist=( $(find . -maxdepth 1 -type f) ) #array of files
awk -v count="${#filelist[@]}" '{value[$1]++}END{for(i in value){
if(value[i]==count){printf "Value %d is found in all files\n",i}}}' "${filelist[@]}"

注意

  1. 我们使用-v count="${#filelist[@]}"将总文件数传递给awk注意数组开头的#给出了元素数。
  2. value[$1]++增加文件中显示的值的计数。如果初始值为零,它还会创建value[$1]
  3. 此方法 失败 ,如果某个值多次出现在文件中。
  4. 带有awk的END块仅在最后执行,即在处理完所有文件的每个记录之后。

答案 1 :(得分:0)

如果您可以在一个文件中多次使用相同的值,我们需要注意每个文件只计算一次。

GNU awk的一些变体(ARGIND需要它可用。可以通过检查FILENAME来模拟,但这甚至更难。)

gawk '{ A[$0] = or(A[$0], lshift(1, ARGIND-1))  }  
      END { for (x in A) if (A[x] == lshift(1, ARGIND) - 1) print x }'
      file1 file2 file3

数组A由值(行)键控,并保存已找到行的文件的位图。对于每行读取,我们设置位号ARGIND-1(因为ARGIND以1开头)。

在输入结束时,遍历所有已保存的行,如果位图全部为1,则打印它们(最多可达到所看到的文件数)。

gawk 'ARGIND > LASTIND { 
            LASTIND = ARGIND; for (x in CURR) { ALL[x] += 1; delete CURR[x] } 
      } 
      { CURR[$0] = 1 }
      END { for (x in CURR) ALL[x] += 1; 
            for (x in ALL) if (ALL[x] == ARGIND) print x 
      }' file1 file2 file3

这里,当遇到一行时,设置数组CURR中的相应元素(中间部分)。当文件编号更改(ARGIND > LASTIND)时,对于ALL中设置的所有值,数组CURR中的值会增加,后者将被清除。在输入的END处,ALL中的值将针对最后一个文件进行更新,并根据文件总数检查总计数,打印出现在所有文件中的文件。

对于大输入,位图方法可能稍快一些,因为它不涉及创建和遍历临时数组,但它可以处理的文件数量受位操作可以处理的位数限制(似乎在64位Linux上大约有50个。)

在这两种情况下,由此产生的打印输出基本上是随机顺序,因为关联数组不会保留排序。

答案 2 :(得分:0)

我会假设它是重要的问题,而不是实施语言,所以这里使用perl作为替代方案:

#! /usr/bin/perl

use strict;

my %elements=();    
my $filecount=@ARGV;

while(<>) {
  $elements{$_}->{$ARGV}++;
};

print grep {!/^$/} map {
    "$_" if (keys %{ $elements{$_} } == $filecount)
} (keys %elements);

while循环构建哈希散列(又名&#34; HoH&#34;。有关详细信息,请参阅man perldscman perllol。另请参阅下面的示例) ,顶级键是每个输入文件的每一行,第二级键是值出现的文件的名称。

grep ... map {...}返回每个顶级键,其中出现的文件数等于输入文件的数量

这里是数据结构的样子,使用你给ilkkachu的例子:

{
  'A' => { 'file1' => 1 },
  'B' => { 'file2' => 1 },
  'C' => { 'file1' => 1, 'file2' => 1, 'file3' => 1 },
  'E' => { 'file2' => 1 },
  'F' => { 'file1' => 1 },
  'K' => { 'file3' => 1 },
  'L' => { 'file3' => 1 }
}

请注意,如果单个文件中出现任何重复项,则该事实存储在此结构中并可以进行检查。

在此特定示例中,grep之前的map并非严格要求,但如果您希望将结果存储在数组中以供进一步处理而不是立即打印,则非常有用。

使用grep,它只返回一个匹配元素的数组,或者在这种情况下只返回单个值C。没有它,它返回一个空字符串数组加上匹配的元素。例如("", "", "", "", "C", "", "")。实际上,他们最后会返回带有换行符(\n)的元素,因为我在chomp循环中没有使用while,因为我知道我正在打印他们直接。在大多数程序中,我使用chomp来删除换行符和/或回车。