2

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

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:例如基于另一列的值打印列)?如果不是,是否有另一种方法可以使用 bash 有效地做到这一点?

4

3 回答 3

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{,}
于 2013-11-14T09:53:56.017 回答
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
于 2013-11-14T10:21:55.547 回答
1

一个很长的解决方案。
将列转换为行

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
于 2013-11-14T10:13:21.097 回答