我有一个看起来像这样的文件:
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有效地做到这一点?
答案 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