J'ai un gros fichier contenant des données comme celle-ci:
a 23
b 8
a 22
b 1
Je veux pouvoir obtenir ceci:
a 45
b 9
Je peux d’abord trier ce fichier puis le faire en Python en l’analysant une fois. Quel est un bon moyen direct en ligne de commande de faire cela?
Edit: La solution moderne (GNU/Linux), comme mentionné dans les commentaires il y a quelques années ;-).
awk '{
arr[$1]+=$2
}
END {
for (key in arr) printf("%s\t%s\n", key, arr[key])
}' file \
| sort -k1,1
La solution initialement publiée, basée sur les anciennes options Unix sort
:
awk '{
arr[$1]+=$2
}
END {
for (key in arr) printf("%s\t%s\n", key, arr[key])
}' file \
| sort +0n -1
J'espère que ça aide.
Ce one-liner Perl semble faire le travail:
Perl -nle '($k, $v) = split; $s{$k} += $v; END {$, = " "; foreach $k (sort keys %s) {print $k, $s{$k}}}' inputfile
Pas besoin de awk ici, ni même de trier - si vous avez Bash 4.0, vous pouvez utiliser des tableaux associatifs:
#!/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
... ou si vous triez d'abord le fichier (ce qui sera plus efficace en termes de mémoire; GNU sort est capable d'effectuer des astuces pour trier des fichiers plus volumineux que la mémoire, ce qui est un script naïf python ou Shell - ce n’est généralement pas le cas), vous pouvez le faire d’une manière qui fonctionnera dans les anciennes versions (les versions suivantes devraient fonctionner jusqu’à 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"
Une manière en utilisant Perl
:
Perl -ane '
next unless @F == 2;
$h{ $F[0] } += $F[1];
END {
printf qq[%s %d\n], $_, $h{ $_ } for sort keys %h;
}
' infile
Contenu de infile
:
a 23
b 8
a 22
b 1
Sortie:
a 45
b 9
Avec GNU awk (versions inférieures à 4):
WHINY_USERS= awk 'END {
for (E in a)
print E, a[E]
}
{ a[$1] += $2 }' infile
Avec GNU awk > = 4:
awk 'END {
PROCINFO["sorted_in"] = "@ind_str_asc"
for (E in a)
print E, a[E]
}
{ a[$1] += $2 }' infile
Ceci peut être facilement réalisé avec le liner unique suivant:
cat /path/to/file | termsql "SELECT col0, SUM(col1) FROM tbl GROUP BY col0"
Ou.
termsql -i /path/to/file "SELECT col0, SUM(col1) FROM tbl GROUP BY col0"
Ici, un paquet Python, termsql , est utilisé, qui encapsule SQLite. Notez qu’à l’heure actuelle, il n’est pas transféré vers PyPI , et ne peut également être installé que sur l’ensemble du système (setup.py
est un peu endommagé), comme:
Sudo pip install https://github.com/tobimensch/termsql/archive/master.Zip