我怎样才能快速汇总一个文件中的所有数字?

我有一个包含几千个数字的文件,每个数字都在它自己的行上:

34 42 11 6 2 99 ... 

我正在寻找一个脚本,将打印文件中的所有数字的总和。 我有一个解决scheme,但效率不高。 (这需要几分钟的时间。)我正在寻找一个更有效的解决scheme。 有什么build议么?

对于Perl而言,它与Ayman Hourieh的答案中的awk解决scheme基本相同:

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

如果您好奇Perl单线程所做的事情,您可以对它们进行sorting:

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

结果是一个更详细的程序版本,没有人会自己写:

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

只是为了咯咯地笑,我试着用一个包含1,000,000个数字的文件(范围在0 – 9,999)。 在我的Mac Pro上,它实际上即时返回。 这太糟糕了,因为我希望使用mmap会非常快,但是它只是同一时间:

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

你可以使用awk:

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

只是为了好玩,让我们来衡量它:

 $ 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<random_numbers;echo $s; } 16379866392 real 0m9.309s user 0m8.404s sys 0m0.887s $ time { s=0;while read l; do ((s+=l));done<random_numbers;echo $s; } 16379866392 real 0m7.191s user 0m6.402s sys 0m0.776s $ time { sed ':a;N;s/\n/+/;ta' random_numbers|bc; } ^C real 4m53.413s user 4m52.584s sys 0m0.052s 

5分钟后我中止了赛跑

迄今为止,解决scheme都没有使用paste 。 这里有一个:

 paste -sd+ filename | bc 

例如,计算Σn,其中1 <= n <= 100000:

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

(为了好奇, seq n会打印一个从1n的数字序列,给定一个正数n 。)

这工作:

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

另一个select是使用jq

 $ seq 10|jq -s add 55 

-s (– --slurp )将input行读入数组。

这是直Bash:

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

这是另外一个class轮

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

这假定这些数字是整数。 如果您需要小数点,请尝试

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

调整2到所需的小数位数。

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

(和brian d foy的答案一样,没有'END')

为了好玩,让我们用PDL ,Perl的数组math引擎来做吧!

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

rcols将列读入matrix(本例中为1D), sum (惊奇)将matrix的所有元素相加。

这里是一个使用python和一个生成器expression式的解决scheme。 在我旧的笔记本电脑上testing了一百万个数字。

 time python -c "import sys; print sum((float(l) for l in sys.stdin))" < file real 0m0.619s user 0m0.512s sys 0m0.028s 
 sed ':a;N;s/\n/+/;ta' file|bc 

我更喜欢使用R这个:

 $ R -e 'sum(scan("filename"))' 

我不知道你是否可以比这更好,考虑到你需要通读整个文件。

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

另一个乐趣

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

或者只是另一个bash

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

但awk解决scheme可能是最好的,因为它是最紧凑的。

 $ perl -MList::Util=sum -le 'print sum <>' nums.txt 

用Ruby:

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

更简洁:

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

我更喜欢使用GNU datamash进行这样的任务,因为它比perl或awk更简洁明了。 例如

 datamash sum 1 < myfile 

其中1表示第一列数据。

这是另一个:

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

C总是赢得速度:

 #include <stdio.h> #include <stdlib.h> 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; } 

时间为1M数字(相同的机器/input作为我的Python的答案):

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

您可以使用Alacon – Alasql数据库的命令行实用程序。

它适用于Node.js,所以你需要安装Node.js ,然后安装Alasql包:

要从TXT文件计算总和,您可以使用以下命令:

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

Perl 6

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

只是为了荒谬:

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

我没有testing过,但它应该工作:

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

如果bc不处理EOF和EOL,则可能必须在bc之前向string中添加“\ n”(如通过echo)。