我有一个场景,我想对多列求和
文件中的数据是:
ID|NAME|SAL|COST|PER|TAG
1|A|10|10|20|10|
1|B|10|15|20|10|
1|C|10|17|25|80|
1|D|115|110|20|100|
1|E|10|10|10|10|
我想要 COLUMN - SAL | 的总和成本| PER |标签
我用简单的命令做了一个,但如何通过创建函数来做到这一点
awk '{FS="|"}{s+=$3}END{print s}' file.txt
该函数应该参数化,以便当我传递列名称时它应该计算该列的总和
总和列可能有所不同。它们可能是这样的要求,例如只需要两列总和,那么它应该采用两列名称并处理该总和
答案1
使用awk
(并丢失输入字符串中的空格)
myv='SAL|COST|PER|TAG'
awk -v ar="$myv" '
BEGIN{FS="|"; getline; for (i=1;i<=NF;i++) {if ($i ~ ar) head[i]=0;title[i]=$i}}
NF>1{for (h in head) head[h]+=$h}
END{for (h in head) print title[h]":\t"head[h]}
' file
这假设正则表达式匹配是唯一的。如果没有那么...
myv='SAL|COST|PER|TAG'
awk -v ar="$myv" '
BEGIN{FS="|"; getline; for (i=1;i<=NF;i++) head[$i]=i; split(ar,titles,"|")}
NF>1{for (i=1; i<=NF; i++) val[i]+=$i}
END{for (t in titles) print titles[t]":\t"val[head[titles[t]]]}
' file
输出
SAL: 155
COST: 162
PER: 95
TAG: 210
答案2
使用足够新的版本磨坊主
$ mlr --csvlite --allow-ragged-csv-input --fs '|' stats1 -a sum -f SAL file.txt
SAL_sum
155
(仅需要最新版本,因为您的输入是衣衫褴褛即有一个尾随空列,标题中没有相应的名称)。您可以通过将多个列的名称作为逗号分隔列表传递给选项,轻松对 Miller 中的多个列求和-f
:
... stats1 -a sum -f SAL,COST,PER,TAG ...
类似地与GNU 数据混合
$ datamash -Ht '|' sum SAL,COST,PER,TAG < file.txt
sum(SAL)|sum(COST)|sum(PER)|sum(TAG)
155|162|95|210
答案3
从中汲取灵感https://stackoverflow.com/a/32616101:
$ col=SAL
$ colnum=$(awk -v RS='|' '/'$col'/{ print NR; exit}' testfile)
$ awk '{FS="|"}{s+='$colnum'}END{print s}' testfile
18
这里的技巧是引用:变量位于单引号之外。
这很容易封装到脚本中sumcols.sh
:
#!/bin/bash
FILE="$1"
COLUMNS="${@:2}"
for col in $COLUMNS; do
colnum=$(awk -v RS='|' '/'$col'/{ print NR; exit}' $FILE)
awk '{FS="|"}{s+='$colnum'}END{print "'$col' ", s}' $FILE
done | column -t
使用要处理的文件作为第一个位置参数来调用它,并跟随要处理的列。例如:
$ ./sumcols.sh testfile SAL COST
SAL 18
COST 24