在这里,一步 pass 的 awk 解决方案 -
awk 'NR==FNR{a = a + $2;next} {c = ($2/a)*100;print $1,$2,c }' file file
[jaypal:~/Temp] cat file
1 10
2 10
3 20
4 40
[jaypal:~/Temp] awk 'NR==FNR{a = a + $2;next} {c = ($2/a)*100;print $1,$2,c }' file file
1 10 12.5
2 10 12.5
3 20 25
4 40 50
Update:如果输出中需要制表符,则只需将 OFS 变量设置为“\t”。
[jaypal:~/Temp] awk -v OFS="\t" 'NR==FNR{a = a + $2;next} {c = ($2/a)*100;print $1,$2,c }' file file
1 10 12.5
2 10 12.5
3 20 25
4 40 50
模式 {action} 语句的突破:
第一个模式是NR==FNR
。 FNR 是 awk 的内置变量,用于跟踪给定文件中的记录数(默认情况下用换行符分隔)。因此,在我们的例子中,FNR 为 4。NR 与 FNR 类似,但它不会重置为 0。它会继续增长。所以在我们的例子中 NR 是 8。
此模式仅适用于前 4 条记录,而这正是我们想要的。仔细阅读 4 条记录后,我们将总计分配给一个变量a
。请注意,我们没有初始化它。在awk
我们不必这样做。但是,如果整个第 2 列都是 0,这就会中断。因此,您可以通过在第二个操作语句中放置 if 语句来处理它,即仅当 a > 0 时才进行除法,否则除以 0 或其他值。
next
之所以需要,是因为我们并不真正希望执行第二个模式 {action} 语句。next
告诉 awk 停止进一步的操作并移至下一条记录。
一旦解析了四个记录,下一个模式{action}就开始,这非常简单。计算百分比并打印第 1 列和第 2 列以及它们旁边的百分比。
Note: 正如 @lhf 在评论中提到的,只有当文件中有数据集时,这一行才有效。如果通过管道传递数据,它将不起作用。
在评论中,正在讨论如何做到这一点awk one-liner
从 a 获取输入pipe
代替file
。我能想到的唯一方法是将列值存储在array
然后使用for loop
吐出每个值及其百分比。
Now arrays
in awk
are associative
并且永远不会按顺序排列,即从数组中取出值的顺序不会与它们进入的顺序相同。因此,如果可以的话,那么下面的一行应该可以工作。
[jaypal:~/Temp] cat file
1 10
2 10
3 20
4 40
[jaypal:~/Temp] cat file | awk '{b[$1]=$2;sum=sum+$2} END{for (i in b) print i,b[i],(b[i]/sum)*100}'
2 10 12.5
3 20 25
4 40 50
1 10 12.5
为了让它们按顺序排列,您可以将结果通过管道传递给sort
.
[jaypal:~/Temp] cat file | awk '{b[$1]=$2;sum=sum+$2} END{for (i in b) print i,b[i],(b[i]/sum)*100}' | sort -n
1 10 12.5
2 10 12.5
3 20 25
4 40 50