448 votes

Commande Bash pour additionner une colonne de nombres

Je veux une commande bash dans laquelle je peux placer un pipe qui fera la somme d'une colonne de nombres. Je veux juste une commande rapide qui fasse quelque chose comme ceci :

cat FileWithColumnOfNumbers.txt | sum

0 votes

0 votes

Pour info, le awk La solution est à la fois plus facile à retenir et environ 2x plus rapide (voir aquí y aquí ).

996voto

Dimitre Radoulov Points 9185

Utilisation d'un fichier existant :

paste -sd+ infile | bc

Utilisation de stdin :

<cmd> | paste -sd+ | bc

Éditer : Avec quelques pâte vous devez être plus explicite lorsque vous lisez les données de l'application stdin :

<cmd> | paste -sd+ - | bc

Options utilisées :

-s (serial) - fusionne toutes les lignes en une seule ligne

-d - utiliser un délimiteur autre que celui par défaut (le caractère + dans ce cas)

63 votes

Il devrait y avoir un badge pour ça.

11 votes

Juste pour info, le -s est dans GNU paste ; il n'est pas pris en charge par Mac OS X 10.7.4 paste . Toutefois, depuis la spécification POSIX 2008 de paste soutient -s Il s'agit d'un défaut de la version Mac OS X.

21 votes

Pour info, pour OS X, j'ai dû ajouter un - à la fin de la commande coller pour que cela fonctionne sous OS X 10.6.8.

216voto

ghostdog74 Points 86060

J'aime la réponse choisie. Cependant, il a tendance à être plus lent que awk puisque deux outils sont nécessaires pour faire le travail.

$ wc -l file
49999998 file

$ time paste -sd+ file | bc
1448700364

real    1m36.960s
user    1m24.515s
sys     0m1.772s

$ time awk '{s+=$1}END{print s}' file
1448700364

real    0m45.476s
user    0m40.756s
sys     0m0.287s

2 votes

Bon point ! Sous SunOS 5.8 bc, même le core dumps avec un fichier d'entrée aussi gros (voir mon post ci-dessous).

4 votes

Awk est l'outil approprié pour ce travail ! La solution bc est correcte mais que se passe-t-il lorsque vous devez additionner deux colonnes ou peut-être filtrer les nombres négatifs. Avec awk, vous pouvez facilement et judicieusement ajouter une logique supplémentaire, avec la solution bc, vous finissez par passer par une autre commande (cut ou grep).

0 votes

@radoulov. merci. je ne savais pas que le bc de Solaris avait des difficultés avec les grosses entrées.

64voto

Jonathan Leffler Points 299946

Est-ce que deux lignes comptent ?

awk '{ sum += $1; }
     END { print sum; }' "$@"

Vous pouvez alors l'utiliser sans le "cat" superflu :

sum < FileWithColumnOfNumbers.txt
sum   FileWithColumnOfNumbers.txt

Pour info : sous MacOS X, vous pouvez le faire avec une ligne unique :

awk '{ sum += $1; } END { print sum; }' "$@"

0 votes

@jskaggz - voir ma réponse pour une version Perl un peu plus courte/simple :)

9 votes

L'awk one-liner fonctionne sur tous les awk que j'ai essayés, pas seulement sur OS X

0 votes

@unhammer : Je n'ai pas accès à la 7ème édition d'UNIX et sa version de awk plus. Je ne sais pas si la ligne unique aurait fonctionné là, mais je n'ai pas essayé à l'époque où j'ai appris awk . Vous avez probablement raison ; toutes les versions actuelles de awk sont susceptibles d'accepter la phrase unique.

19voto

Dimitre Radoulov Points 9185

[suite aux commentaires de ghostdog74]

bash-2.03$ uname -sr
SunOS 5.8

bash-2.03$ perl -le 'print for 1..49999998' > infile

bash-2.03$ wc -l infile
 49999998 infile

bash-2.03$  time paste -sd+ infile | bc
bundling space exceeded on line 1, teletype
Broken Pipe

real    0m0.062s
user    0m0.010s
sys     0m0.010s

bash-2.03$ time nawk '{s+=$1}END{print s}' infile
1249999925000001

real    2m0.042s
user    1m59.220s
sys     0m0.590s
bash-2.03$ time /usr/xpg4/bin/awk '{s+=$1}END{print s}' infile
1249999925000001

real    2m27.260s
user    2m26.230s
sys     0m0.660s

bash-2.03$ time perl -nle'
  $s += $_; END { print $s }
   ' infile
1.249999925e+15

real    1m34.663s
user    1m33.710s
sys     0m0.650s

1 votes

+1 pour avoir signalé le coredump dans la version bc de ceci. J'hésite à changer la "réponse" de cette question car la version bc fonctionne bien pour moi (je totalise 30 numéros).

0 votes

+1 pour la version perl - la plus rapide sur mon ubuntu 12.04

0 votes

+1 pour la version Perl travaillant sur les nombres à virgule flottante. Aussi, pour exactement ce que je cherchais, voici la somme d'une colonne particulière (colonne 2 indexée à partir de zéro) : perl -nle '$s += (split)[2]; END { print $s }' foo.txt ou en utilisant des tuyaux : cat foo.txt | perl -nle '$s += (split)[2]; END { print $s }' .

15voto

DVK Points 63282

Vous pouvez utiliser bc (calculatrice). En supposant que votre fichier avec des #s s'appelle "n" :

$ cat n
1
2
3
$ (cat n | tr "\012" "+" ; echo "0") | bc 
6

Le site tr transforme tous les retours à la ligne en "+" ; ensuite, nous ajoutons 0 après le dernier plus, puis nous faisons passer l'expression ( 1+2+3+0 ) à la calculatrice

Ou, si vous êtes capable d'utiliser awk ou perl, voici un exemple en Perl :

$perl -nle '$sum += $_ } END { print $sum' n
6

2 votes

Pour le perl, cela fonctionne aussi : perl -nle '$s+=$_}{print $s' C'est un peu plus simple. :)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X