GROUP BY / SUM del shell

Tengo un archivo grande que contiene datos como este:

a 23 b 8 a 22 b 1 

Quiero ser capaz de obtener esto:

 a 45 b 9 

Primero puedo ordenar este archivo y luego hacerlo en Python escaneando el archivo una vez. ¿Cuál es una buena forma directa de hacer línea de comandos?

Edición: la solución moderna (GNU / Linux), como se mencionó en los comentarios hace años ;-).

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

La solución publicada originalmente, basada en las antiguas opciones de sort Unix:

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

Espero que esto ayude.

No necesita awk aquí, ni siquiera ordenar: si tiene Bash 4.0, puede usar matrices asociativas:

 #!/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 

… o, si ordena primero el archivo (que será más eficiente en cuanto a la memoria; GNU sort puede hacer trucos para clasificar archivos más grandes que la memoria, lo que normalmente es una secuencia de comandos ingenua, ya sea en awk, python o shell) no), puede hacerlo de una manera que funcione en versiones anteriores (espero que lo siguiente funcione a través de 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" 

Este trazador de líneas Perl parece hacer el trabajo:

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

Una forma de usar perl :

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

Contenido de infile :

 a 23 b 8 a 22 b 1 

Salida:

 a 45 b 9 

Con GNU awk (versiones menos de 4):

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

Con GNU awk > = 4:

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

Esto se puede lograr fácilmente con el siguiente revestimiento único:

 cat /path/to/file | termsql "SELECT col0, SUM(col1) FROM tbl GROUP BY col0" 

O.

 termsql -i /path/to/file "SELECT col0, SUM(col1) FROM tbl GROUP BY col0" 

Aquí se usa un paquete de Python, termsql , que es un envoltorio alrededor de SQLite. Tenga en cuenta que actualmente no se carga en PyPI , y solo se puede instalar en todo el sistema ( setup.py está un poco roto), como:

 sudo pip install https://github.com/tobimensch/termsql/archive/master.zip