awk:根据列值提取列

时间:2013-11-14 09:40:27

标签: bash awk multiple-columns

我有一个看起来像这样的文件:

C1 C2 C3 C4 C5
0 0 0 0 0
0 1 0 0 0
0 0 0 1 0
0 0 0 0 0

但更大......

我想只提取其中包含全部0的列,因此我的输出文件应如下所示:

C1 C3 C5
0 0 0
0 0 0
0 0 0
0 0 0

这可以通过简单的awk单行(例如类似于awk: print columns based on values of another column)来完成吗?如果不是,有没有其他方法可以使用bash有效地做到这一点?

3 个答案:

答案 0 :(得分:4)

尝试关注awk

awk 'NR==1 {next} NR==FNR { for(i=1;i<=NF;i++) sum[i]+=$i; next } { for(i=1;i<=NF;i++) if (sum[i]==0) printf " %s", $i; print "" }' file{,}

输出

 C1 C3 C5
 0 0 0
 0 0 0
 0 0 0
 0 0 0

这里的想法是迭代文件两次。一旦它计算了所有列的总和,并且在下一次迭代中,它只打印总和等于零的列。

这假设所有列条目仅正数


另一个,可能更好,方法是在列中的任何条目非零时设置标志。然后只打印那些对应标志为零的列。

awk 'NR==1 {next} NR==FNR { for(i=1;i<=NF;i++) if ($i) flag[i]=1; next } { for(i=1;i<=NF;i++) if (!flag[i]) printf " %s", $i; print "" }' file{,}

此方法允许正数和负数,并删除任何限制。

或者 @fedorqui 在评论

中的建议
awk 'NR==1 {next} NR==FNR { for(i=1;i<=NF;i++) if ($i) flag[i]=1; next } { for(i=1;i<=NF;i++) if (flag[i]) $i="" } 1' file{,}

答案 1 :(得分:2)

适用于带有负数或其他字符串的数据,例如“foo”或“bar

单行:

awk 'NR==1{next}NR==FNR{while(++i<=NF)if($i!="0")k[i];i=0;next}{while(++x<=NF)if(!(x in k))printf "%s ",$x;x=0;print ""}' file file

更具可读性:

awk 'NR==1{next}
     NR==FNR{while(++i<=NF)if($i!="0")k[i];i=0;next}
     {while(++x<=NF)
         if(!(x in k)) printf "%s ",$x
      x=0
      print ""}' file file

答案 2 :(得分:1)

一个loooong解决方案。
将列转换为行

awk '{
       for (f = 1; f <= NF; f++) { a[NR, f] = $f }
     }
     NF > nf { nf = NF }
     END {
       for (f = 1; f <= nf; f++) {
           for (r = 1; r <= NR; r++) {
               printf a[r, f] (r==NR ? RS : FS)
           }
       }
    }' file >tmp1

仅打印仅包含0

的行
awk '{for (i=2;i<=NF;i++) f+=$i} !f; {f=0}' tmp1 >tmp2

转换回来

awk '{
       for (f = 1; f <= NF; f++) { a[NR, f] = $f }
     }
     NF > nf { nf = NF }
     END {
       for (f = 1; f <= nf; f++) {
           for (r = 1; r <= NR; r++) {
               printf a[r, f] (r==NR ? RS : FS)
           }
       }
    }' tmp2

给出

C1 C3 C5
0 0 0
0 0 0
0 0 0
0 0 0