Como posso resumir rapidamente todos os números em um arquivo?

Eu tenho um arquivo que contém vários milhares de números, cada um em sua própria linha:

34 42 11 6 2 99 ... 

Eu estou olhando para escrever um script que irá imprimir a sum de todos os números no arquivo. Eu tenho uma solução, mas não é muito eficiente. (Leva vários minutos para ser executado). Estou procurando uma solução mais eficiente. Alguma sugestão?

Para um perl one-liner, é basicamente a mesma coisa que a solução awk na resposta de Ayman Hourieh :

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

Se você está curioso para saber o que os Perl one-liners fazem, você pode aplicá-los:

  % perl -MO=Deparse -nle '$sum += $_ } END { print $sum' 

O resultado é uma versão mais detalhada do programa, de uma forma que ninguém jamais escreveria por conta própria:

 BEGIN { $/ = "\n"; $\ = "\n"; } LINE: while (defined($_ = )) { chomp $_; $sum += $_; } sub END { print $sum; } -e syntax OK 

Só por risos, eu tentei isso com um arquivo contendo 1.000.000 de números (no intervalo de 0 a 9.999). No meu Mac Pro, ele retorna virtualmente instantaneamente. Isso é muito ruim, porque eu estava esperando usar o mmap seria muito rápido, mas é apenas o mesmo tempo:

 use 5.010; use File::Map qw(map_file); map_file my $map, $ARGV[0]; $sum += $1 while $map =~ m/(\d+)/g; say $sum; 

Você pode usar o awk:

 awk '{ sum += $1 } END { print sum }' file 

Nenhuma das soluções até agora usa paste . Aqui está um:

 paste -sd+ filename | bc 

Por exemplo, calcule Σn onde 1 <= n <= 100000:

 $ seq 100000 | paste -sd+ | bc -l 5000050000 

(Para os curiosos, seq n imprimiria uma sequência de números de 1 a n dado um número positivo n .)

Apenas por diversão, vamos fazer um benchmark:

 $ for ((i=0; i<1000000; i++)) ; do echo $RANDOM; done > random_numbers $ time perl -nle '$sum += $_ } END { print $sum' random_numbers 16379866392 real 0m0.226s user 0m0.219s sys 0m0.002s $ time awk '{ sum += $1 } END { print sum }' random_numbers 16379866392 real 0m0.311s user 0m0.304s sys 0m0.005s $ time { { tr "\n" + < random_numbers ; echo 0; } | bc; } 16379866392 real 0m0.445s user 0m0.438s sys 0m0.024s $ time { s=0;while read l; do s=$((s+$l));done 

Eu abortei o sed run depois de 5 minutos

Isso funciona:

 { tr '\n' +; echo 0; } < file.txt | bc 

Outra opção é usar o jq :

 $ seq 10|jq -s add 55 

-s ( --slurp ) lê as linhas de input em um array.

Este é um Bash direto:

 sum=0 while read -r line do (( sum += line )) done < file echo $sum 

Aqui está outro one-liner

 ( echo 0 ; sed 's/$/ +/' foo ; echo p ) | dc 

Isso pressupõe que os números são inteiros. Se você precisar de decimais, tente

 ( echo 0 2k ; sed 's/$/ +/' foo ; echo p ) | dc 

Ajuste 2 ao número de decimais necessário.

Eu prefiro usar o datamash GNU para essas tarefas porque é mais sucinto e legível do que perl ou awk. Por exemplo

 datamash sum 1 < myfile 

onde 1 denota a primeira coluna de dados.

 cat nums | perl -ne '$sum += $_ } { print $sum' 

(igual à resposta de brian d foy, sem ‘END’)

Apenas por diversão, vamos fazer isso com o PDL , o mecanismo matemático de matriz do Perl!

 perl -MPDL -E 'say rcols(shift)->sum' datafile 

rcols lê colunas em uma matriz (1D neste caso) e sum (surpresa) sum todo o elemento da matriz.

Aqui está uma solução usando python com uma expressão de gerador. Testado com um milhão de números no meu velho laptop cruddy.

 time python -c "import sys; print sum((float(l) for l in sys.stdin))" < file real 0m0.619s user 0m0.512s sys 0m0.028s 

Eu prefiro usar o R ​​para isso:

 $ R -e 'sum(scan("filename"))' 
 sed ':a;N;s/\n/+/;ta' file|bc 
 $ perl -MList::Util=sum -le 'print sum <>' nums.txt 

Perl 6

 say sum lines 
 ~$ perl6 -e '.say for 0..1000000' > test.in ~$ perl6 -e 'say sum lines' < test.in 500000500000 

Outro para se divertir

 sum=0;for i in $(cat file);do sum=$((sum+$i));done;echo $sum 

ou apenas outra festa

 s=0;while read l; do s=$((s+$l));done 

Mas a solução awk é provavelmente a melhor, já que é mais compacta.

Com Ruby:

 ruby -e "File.read('file.txt').split.inject(0){|mem, obj| mem += obj.to_f}" 

Mais sucinto:

 # Ruby ruby -e 'puts open("random_numbers").map(&:to_i).reduce(:+)' # Python python -c 'print(sum(int(l) for l in open("random_numbers")))' 

Eu não sei se você pode ficar muito melhor do que isso, considerando que você precisa ler todo o arquivo.

 $sum = 0; while(<>){ $sum += $_; } print $sum; 

Eu não testei isso, mas deve funcionar:

 cat f | tr "\n" "+" | sed 's/+$/\n/' | bc 

Você pode ter que adicionar “\ n” à string antes de bc (como via echo) se bc não tratar EOF e EOL …

Aqui está outro:

 open(FIL, "a.txt"); my $sum = 0; foreach(  ) {chomp; $sum += $_;} close(FIL); print "Sum = $sum\n"; 

Você pode fazer isso com o Alacon – utilitário de linha de comando para o database Alasql .

Ele funciona com o Node.js, portanto, é necessário instalar o pacote Node.js e, em seguida, o pacote Alasql :

Para calcular a sum do arquivo TXT, você pode usar o seguinte comando:

 > node alacon "SELECT VALUE SUM([0]) FROM TXT('mydata.txt')" 

Não é mais fácil replace todas as novas linhas por + , adicionar um 0 e enviá-lo ao interpretador Ruby ?

 (sed -e "s/$/+/" file; echo 0)|irb 

Se você não tiver irb , você pode enviá-lo para bc , mas você tem que remover todas as novas linhas, exceto a última (de echo ). É melhor usar tr para isso, a menos que você tenha um PhD em sed .

 (sed -e "s/$/+/" file|tr -d "\n"; echo 0)|bc 

Apenas para ser ridículo:

 cat f | tr "\n" "+" | perl -pne chop | R --vanilla --slave 

C sempre ganha por velocidade:

 #include  #include  int main(int argc, char **argv) { ssize_t read; char *line = NULL; size_t len = 0; double sum = 0.0; while (read = getline(&line, &len, stdin) != -1) { sum += atof(line); } printf("%f", sum); return 0; } 

Tempo para números 1M (mesma máquina / input da minha resposta python):

 $ gcc sum.c -o sum && time ./sum < numbers 5003371677.000000 real 0m0.188s user 0m0.180s sys 0m0.000s