给你,一个 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
更新:如果输出中需要选项卡,则只需将 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 else 表示除以 0 或其他内容时才进行除法。
next 是必需的,因为我们真的不希望执行第二个模式 {action} 语句。 next 告诉 awk 停止进一步的操作并移动到下一条记录。
一旦四个记录被解析,下一个模式{action}就开始了,这非常简单。执行百分比并打印第 1 列和第 2 列以及它们旁边的百分比。
注意: 正如@lhf 在评论中提到的那样,只要您将数据集保存在文件中,这种单行就可以工作。如果您通过管道传递数据,它将无法工作。
在 cmets 中,正在讨论如何让 awk one-liner 从 pipe 而非 file 获取输入。那么我能想到的唯一方法是将列值存储在array 中,然后使用for loop 将每个值连同它们的百分比一起吐出。
现在awk 中的arrays 是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