【问题标题】:AWK Compare 2 columns in 2 files, print if one is equal while other is notAWK比较2个文件中的2列,如果一个相等而另一个不相等则打印
【发布时间】:2017-09-12 20:44:17
【问题描述】:

如果两个文件中的第 1 列匹配但第 3 列不匹配,我正在尝试打印文件一中的所有行 + 文件二中的第 3 列。

例子:

File1  
John  12  Mango  
David  13  Apple  
Jack 14  Orange  

File2   
John  12  Grape  
David  13  Apple  
Jack 14  Apple  

输出

John 12 Mango Grape  
Jack 14 Orange Apple

我尝试了不同的 awk 命令,但它们在两列都匹配时有效,但当只有一列匹配而其他列不匹配时,我需要打印。我对脚本和 Unix 命令相当陌生,我希望能对所提出的解决方案稍加解释。

【问题讨论】:

    标签: unix awk


    【解决方案1】:
    $ awk 'NR==FNR{a[$1]=$3; next} ($1 in a) && (a[$1] != $3){print $0, a[$1]}' file2 file1
    John 12 Mango Grape
    Jack 14 Orange Apple
    

    【讨论】:

    • 这正是我一直在寻找的,其余的解决方案也可以工作,但这样可以更轻松地修改。 Ed,您能否提供语法细分和/或针对 AWK 的相同用法提出任何教程?
    • 在读取 file2 时创建 $1 到 $3 的数组映射,然后如果 file1 中的 $1 是该数组中的索引,则它将当前 $1 的 $3 与通过相同索引保存在数组中的 $3 进行比较当它从file2中读取时。如果 file1 中的当前 $1 是 file2 中的 $1,并且 file1 中的 $3 值与 fie 1 中的 $3 值不同,则它会打印 file1 中的当前行,然后是 file2 中由 $1 索引的 $3。希望这是有道理的。获取 Arnold RObbins 所著的《Effective Awk Programming, 4th Edition》一书,
    • 问@EdMorton 的一个小问题,如果在匹配之后,我们想打印文件 2 的整行,语法会如何?
    • @Jacek 请发布一个新的后续问题。
    【解决方案2】:

    粘贴 + awk 方法:

    paste <(sort file1) <(sort file2) | awk '$1==$4 && $3!=$6{ print $1,$2,$3,$6 }'
    

    输出:

    Jack 14 Orange Apple
    John 12 Mango Grape
    

    【讨论】:

    • 这是一个有趣的解决方案,但我认为如果任一文件有额外的行并且会导致其余记录的偏移量,它将受到限制。
    【解决方案3】:

    awk 解决方案:

    $ cat tst.awk
    NR==FNR { a[$1" "$2]=$3; next }
    { s=$1" "$2;
      if (s in a && a[s] != $3)
         printf("%s %s %s\n", s, a[s], $3)
    }
    

    跑步:

    $ awk -f tst.awk input1.txt input2.txt
    John 12 Mango Grape
    Jack 14 Orange Apple
    

    编辑通用版本:匹配第 1 列而列 col 不匹配

    $ cat tst2.awk
    BEGIN {col=3}
    NR==FNR { a[$1]=$0; next }
    $1 in a {
       split(a[$1],b," ");
       if ($col!=b[col])
           print a[$1], $col
    }
    

    您甚至可以从 awk 文件中删除 BEGIN 并添加变量 col 命令行,如下所示:

    $ awk -v col=3 -f tst2.awk input1.txt input2.txt
    John 12 Mango Grape
    Jack 14 Orange Apple
    

    【讨论】:

    • 感谢马克的回复。但是有没有一种方法可以专门匹配第一列和第三列中的不匹配?无论第二列。我问的原因是取决于我可能需要更改匹配和不匹配的列的情况,将来我可能会有多达 6 个额外的列。
    • 添加了一个更通用的版本来回答。
    【解决方案4】:

    如果您需要通用解决方案,遵循一个简单的 Perl 脚本(约 30 行),它应该适用于文件中任意数量的列以及作为输入参数给出的匹配/不匹配的列号 -

    use strict;
    
    my @f1; my @f2;
    
    open F, config()->{file1} or die $!;
    while (<F>){
      chomp;
      next unless /\S+/;
      push @f1, [ split /\s+/ ];
    }
    close F;
    
    open F, config()->{file2} or die $!;
    while (<F>){
      chomp;
      next unless /\S+/;
      push @f2, [ split /\s+/ ];
    }
    close F;
    
    my $c1 = config()->{'match_col_num'}-1;
    my $c2 = config()->{'mismatch_col_num'}-1;
    
    for my $l1 (@f1){
      for my $l2 (@f2){
        if ($l1->[$c1] eq $l2->[$c1] and $l1->[$c2] ne $l2->[$c2]){
          print join " ", (@{$l1}, $l2->[$c2]);
          print "\n";
        }
      }
    }
    

    在https://github.com/melezhik/file-compare-columns查看使用信息

    【讨论】:

      猜你喜欢
      • 2018-03-06
      • 2019-11-11
      • 1970-01-01
      • 2012-02-13
      • 1970-01-01
      • 2019-09-08
      • 2020-08-11
      • 2014-05-25
      • 1970-01-01
      相关资源
      最近更新 更多