【问题标题】:Subtructing n number of columns from two files with AWK使用 AWK 从两个文件中减去 n 列
【发布时间】:2013-02-09 13:32:26
【问题描述】:

我有两个包含 N 列的文件

文件1:

A   1   2    3  .......  Na1
B   2   3    4  .......  Nb1

文件2:

A   2   2    4  .......  Na2
B   1   3    4  .......  Nb2

我想要一个输出,其中 File1 的第一列值将从 File2 的第一列中减去,这样直到第 N 列,如下所示:

A  -1   0    -1  ........ (Na1-Na2)
B   1   0     0  ........ (Nb1-Nb2)

如何做到这一点是AWK,或者Linux环境下的Perl脚本?

【问题讨论】:

    标签: linux perl bash awk gawk


    【解决方案1】:

    这已经回答了,但我会添加一个单行。它使用paste 连接文件,使用awk 减去:

    paste file{1,2} | awk '{for (i=1;i<=NF/2;i++) printf "%s ", ($i==$i+0)?$i-$(i+NF/2):$i; print ""}'
    

    验证:

    $ cat file1
    A   1   2    3   4  5
    B   2   3    4   5  6
    
    $ cat file2
    A   2   2    4 10 12 
    B   1   3    4  3 5
    
    $ paste file{1,2} | awk '{for (i=1;i<=NF/2;i++) printf "%s ", ($i==$i+0)?$i-$(i+NF/2):$i; print ""}'
    A -1 0 -1 -6 -7 
    B 1 0 0 2 1
    

    它要求两个文件具有相同的列数。非数字列应位于同一位置。如果不是数字,则打印第一个文件中的值,否则打印差异。

    【讨论】:

      【解决方案2】:

      试试:

      awk '{split($0,S); getline<f; for(i=2; i<=NF; i++) $i-=S[i]}1' OFS='\t' f=file1 file2
      

      【讨论】:

        【解决方案3】:

        这是使用GNU awk 的一种方式。运行如下:

        awk -f script.awk File2 File1 | rev | column -t | rev
        

        script.awk的内容:

        FNR==NR {
            for(i=2;i<=NF;i++) {
                a[$1][i]=$i
            }
            next
        }
        
        {
            for(j=2;j<=NF;j++) {
                $j-=a[$1][j]
            }
        }1
        

        或者,这里是单行:

        awk 'FNR==NR { for(i=2;i<=NF;i++) a[$1][i]=$i; next } { for(j=2;j<=NF;j++) $j-=a[$1][j] }1' File2 File1 | rev | column -t | rev
        

        结果:

        A  -1  0  -1
        B   1  0   0
        

        【讨论】:

          【解决方案4】:
          awk 'FNR==NR{for(i=2;i<=NF;i++)a[FNR"-"i]=$i;next}{printf "\n"$1" ";for(i=2;i<=NF;i++){printf $i-a[FNR"-"i]" "}}' file1 file2
          > cat file1
          A   1   2    3
          B   2   3    4
          > cat file2
          A   2   2    4
          B   1   3    4
          > awk 'FNR==NR{for(i=2;i<=NF;i++)a[FNR"-"i]=$i;next}{printf "\n"$1" ";for(i=2;i<=NF;i++){printf $i-a[FNR"-"i]" "}}' file1 file2 
          A 1 0 1 
          B -1 0 0 
          >
          

          或者把它放在一个文件中

          #!/usr/bin/awk
          FNR==NR{
             for(i=2;i<=NF;i++)
             a[FNR"-"i]=$i;next
               }
             {
              printf "\n"$1" ";
              for(i=2;i<=NF;i++)
              {
               printf $i-a[FNR"-"i]" "
              }
             }
          

          并执行为:

          awk -f file.awk file1 file2
          

          【讨论】:

            【解决方案5】:

            类似这样的:

            use strict;
            use warnings;
            
            my (@fh, @v);
            for (@ARGV) {
              open (my $handle, "<", $_) or die ("$!: $_");
              push @fh, $handle;
            }
            while (@v = map { [split ' ', <$_> ] } @fh and defined shift @{$v[0]}) {
              print join(" ", (shift @{$v[1]}, map { $_ - shift(@{$v[1]}) } @{$v[0]})), "\n";
            }
            close $_ for (@fh);
            

            运行:

             perl script.pl input1 input2
            

            【讨论】:

            • 始终将$! 放在die 字符串中,这样您就知道为什么打开失败了。 split ' ' 几乎总是你想要的,而不是 split /\s+/。 C 风格的for 循环通常最好写成列表迭代器,这里是for my $i (0 .. $#v2) { ... }
            【解决方案6】:

            也许是这样的?恐怕我无法测试这段代码,因为我目前手头没有电脑。

            这个程序需要两个文件的名字作为命令行参数,并将结果输出到STDOUT

            use strict;
            use warnings;
            use autodie;
            
            my @fh;
            for my $filename (@ARGV) {
              open my $fh, '<', $filename;
              push @fh, $fh;
            }
            
            until (grep eof $_, @fh) {
              my @records;
              for my $fh (@fh) {
                my $line = <$fh>;
                chomp $line;
                push @records, [ split ' ', $line ];
              }
            
              $records[0][$_] -= $records[1][$_] for 1 .. $#{$records[0]};
              print "@{$records[0]}\n";
            }
            

            【讨论】:

            • push 后面的逗号不属于那里:-)
            • @ChrisCharley:不知道它是怎么进来的!谢谢。固定。
            猜你喜欢
            • 1970-01-01
            • 2018-07-13
            • 1970-01-01
            • 1970-01-01
            • 2021-12-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2023-04-07
            相关资源
            最近更新 更多