来自shell的GROUP BY / SUM

我有一个包含这样的数据的大文件:

a 23 b 8 a 22 b 1 

我想能够得到这个:

 a 45 b 9 

我可以先对这个文件进行sorting,然后通过扫描文件来完成。 什么是一个好的直接的命令行方式呢?

 awk '{ arr[$1]+=$2 } END { for (key in arr) printf("%s\t%s\n", key, arr[key]) }' file \ | sort +0n -1 

我希望这有帮助。

这里不需要awk,甚至不需要排序 – 如果你有Bash 4.0,你可以使用关联数组:

 #!/bin/bash declare -A values while read key value; do values["$key"]=$(( $value + ${values[$key]:-0} )) done for key in "${!values[@]}"; do printf "%s %s\n" "$key" "${values[$key]}" done 

…或者,如果你首先对文件进行排序(这将更有效地利用内存; GNU排序能够做技巧来对大于内存的文件进行排序,这是一个天真的脚本 – 无论是在awk,python还是shell中)不会),你可以这样做的方式,将在旧版本(我期望以下工作通过bash 2.0):

 #!/bin/bash read cur_key cur_value while read key value; do if [[ $key = "$cur_key" ]] ; then cur_value=$(( cur_value + value )) else printf "%s %s\n" "$cur_key" "$cur_value" cur_key="$key" cur_value="$value" fi done printf "%s %s\n" "$cur_key" "$cur_value" 

这个Perl单线程似乎做的工作:

 perl -nle '($k, $v) = split; $s{$k} += $v; END {$, = " "; foreach $k (sort keys %s) {print $k, $s{$k}}}' inputfile 

一种使用perl

 perl -ane ' next unless @F == 2; $h{ $F[0] } += $F[1]; END { printf qq[%s %d\n], $_, $h{ $_ } for sort keys %h; } ' infile 

infile内容:

 a 23 b 8 a 22 b 1 

输出:

 a 45 b 9 

GNU awk (版本小于4):

 WHINY_USERS= awk 'END { for (E in a) print E, a[E] } { a[$1] += $2 }' infile 

GNU awk > = 4:

 awk 'END { PROCINFO["sorted_in"] = "@ind_str_asc" for (E in a) print E, a[E] } { a[$1] += $2 }' infile