壳命令求和整数,每行一个?

我正在寻找一个命令,将接受作为input多行文本,每行包含一个整数,并输出这些整数的总和。

作为一个背景,我有一个日志文件,其中包括时间测量,所以通过对相关行的grepping,以及一些sed重新格式化我可以列出该文件中的所有时间。 然而,我想要计算一下总数,而且我的脑海里一片空白,我可以用这个中间输出来做任何命令,以便做最后的总和。 我以前一直使用expr ,但是除非它运行在RPN mode ,否则我不认为它会应付这个问题(即使这样也会很棘手)。

我错过了什么? 考虑到可能有几种方法来实现这一目标,即使其他人已经发布了一个不同的解决scheme来完成这个工作,我也会很乐意阅读(并且upvote )任何可行的方法。

相关问题: 最短的命令来计算在Unix上的一列输出的总和? (学分@安德鲁 )


更新 :哇,正如所料,这里有一些很好的答案。 看起来,我一定要作为一个command-line tool awk深入检查!

awk的位应该这样做?

 awk '{s+=$1} END {print s}' mydatafile 

注意:如果要添加超过2 ^ 31(2147483647)的任何内容,某些版本的awk会有一些奇怪的行为。 查看更多背景的评论。 一个build议是使用printf而不是print

 awk '{s+=$1} END {printf "%.0f", s}' mydatafile 

粘贴通常会合并多个文件的行,但也可以用来将文件的各行转换为一行。 分隔符标志允许你传递一个x + x型的公式给bc。

 paste -s -d+ infile | bc 

另外,从标准inputpipe道,

 <commands> | paste -s -d+ - | bc 

Python中的单行版本:

 $ python -c "import sys; print(sum(int(l) for l in sys.stdin))" 

简单的bash:

 $ cat numbers.txt 1 2 3 4 5 6 7 8 9 10 $ sum=0; while read num; do ((sum += num)); done < numbers.txt; echo $sum 55 
 dc -f infile -e '[+z1<r]srz1<rp' 

请注意,以负号为前缀的负数应翻译为dc ,因为它使用_前缀而不是前缀。 例如,通过tr '-' '_' | dc -f- -e '...' tr '-' '_' | dc -f- -e '...'

编辑:由于这个答案得到这么多票“朦胧”,这里是一个详细的解释:

expression式[+z1<r]srz1<rp 执行以下操作 :

 [ interpret everything to the next ] as a string + push two values off the stack, add them and push the result z push the current stack depth 1 push one <r pop two values and execute register r if the original top-of-stack (1) is smaller ] end of the string, will push the whole thing to the stack sr pop a value (the string above) and store it in register r z push the current stack depth again 1 push 1 <r pop two values and execute register r if the original top-of-stack (1) is smaller p print the current top-of-stack 

作为伪代码:

  1. 将“add_top_of_stack”定义为:
    1. 从堆栈中移除两个最高值并将结果添加回去
    2. 如果堆栈有两个或多个值,请recursion运行“add_top_of_stack”
  2. 如果堆栈有两个或更多的值,运行“add_top_of_stack”
  3. 打印结果,现在是堆栈中剩下的唯一项目

要真正理解dc的简单性和强大function,下面是一个可用的Python脚本,它实现了一些来自dc的命令,并执行上述命令的Python版本:

 ### Implement some commands from dc registers = {'r': None} stack = [] def add(): stack.append(stack.pop() + stack.pop()) def z(): stack.append(len(stack)) def less(reg): if stack.pop() < stack.pop(): registers[reg]() def store(reg): registers[reg] = stack.pop() def p(): print stack[-1] ### Python version of the dc command above # The equivalent to -f: read a file and push every line to the stack import fileinput for line in fileinput.input(): stack.append(int(line.strip())) def cmd(): add() z() stack.append(1) less('r') stack.append(cmd) store('r') z() stack.append(1) less('r') p() 

我会对通常认可的解决scheme提出很大的警告:

 awk '{s+=$1} END {print s}' mydatafile # DO NOT USE THIS!! 

这是因为在这种forms下awk使用一个32位有符号整数表示:它将超过2147483647(即2 ^ 31)的和。

更一般的答案(用于求和整数)将是:

 awk '{s+=$1} END {printf "%.0f\n", s}' mydatafile # USE THIS INSTEAD 

PS我想评论第一个答案,但我没有足够的声誉..

纯粹的短打击。

 f=$(cat numbers.txt) echo $(( ${f//$'\n'/+} )) 
 perl -lne '$x += $_; END { print $x; }' < infile.txt 

用jq :

 seq 10 | jq -s 'add' # 'add' is equivalent to 'reduce .[] as $item (0; . + $item)' 

我的十五美分:

 $ cat file.txt | xargs | sed -e 's/\ /+/g' | bc 

例:

 $ cat text 1 2 3 3 4 5 6 78 9 0 1 2 3 4 576 7 4444 $ cat text | xargs | sed -e 's/\ /+/g' | bc 5148 

BASH解决scheme,如果你想使这个命令(例如,如果你需要经常这样做):

 function addnums { TOTAL=0 while read val; do TOTAL=$(($TOTAL+$val)) done echo $TOTAL } 

然后用法:

 cat /tmp/nums | addnums 

简单的打击一个class轮

 $ cat > /tmp/test 1 2 3 4 5 ^D $ echo $(( $(cat /tmp/test | tr "\n" "+" ) 0 )) 

以下在bash中的作品:

 I=0 for N in `cat numbers.txt` do I=`expr $I + $N` done echo $I 

你可以使用num-utils,虽然它可能是你所需要的矫枉过正。 这是一套用于在shell中操纵数字的程序,可以做几件漂亮的事情,当然包括添加它们。 这有点过时了,但是如果你需要做更多的事情,它们还是可以工作的。

http://suso.suso.org/programs/num-utils/

我意识到这是一个古老的问题,但我喜欢这个解决scheme,足以分享它。

 % cat > numbers.txt 1 2 3 4 5 ^D % cat numbers.txt | perl -lpe '$c+=$_}{$_=$c' 15 

如果有兴趣,我会解释它是如何工作的。

纯粹的打击和单行:-)

 $ cat numbers.txt 1 2 3 4 5 6 7 8 9 10 $ I=0; for N in $(cat numbers.txt); do I=$(($I + $N)); done; echo $I 55 
 sed 's/^/.+/' infile | bc | tail -1 

awk '{sum+=$1}END{print sum}'我认为AWK就是你要找的东西

您可以通过将数字列表传递到标准input或通过传递包含数字的文件作为参数来使用此命令。

可选的纯Perl,相当可读,不需要包或选项:

 perl -e "map {$x += $_} <> and print $x" < infile.txt 

对于Ruby恋人

 ruby -e "puts ARGF.map(&:to_i).inject(&:+)" numbers.txt 

如果你觉得舒服,你可以用python来做:

未经testing,只需input:

 out = open("filename").read(); lines = out.split('\n') ints = map(int, lines) s = sum(ints) print s 

塞巴斯蒂安指出了一个单线程的脚本:

 cat filename | python -c"from fileinput import input; print sum(map(int, input()))" 

以下应该工作(假设你的号码是每行的第二个字段)。

 awk 'BEGIN {sum=0} \ {sum=sum + $2} \ END {print "tot:", sum}' Yourinputfile.txt 

一球在球拍:

 racket -e '(define (g) (define i (read)) (if (eof-object? i) empty (cons i (g)))) (foldr + 0 (g))' < numlist.txt 

C(不简化)

 seq 1 10 | tcc -run <(cat << EOF #include <stdio.h> int main(int argc, char** argv) { int sum = 0; int i = 0; while(scanf("%d", &i) == 1) { sum = sum + i; } printf("%d\n", sum); return 0; } EOF) 

我的版本:

 seq -5 10 | xargs printf "- - %s" | xargs | bc 

或者使用awk而不是sed: 算术样本

AWK已经被提及,所以另外我想build议你使用这种语言代替 GREP和SED来扫描原始的日志文件。 一个合适的AWK脚本可以很容易地完成两者的工作,并且可以像Paul和Alf所指出的那样计算出有趣的值。

 $猫n
 2
 4
 2
 7
 8
 9
 $ perl -MList::Util -le 'print List::Util::sum(<>)' < n 32 

或者,您可以在命令行上键入数字:

 $ perl -MList::Util -le 'print List::Util::sum(<>)' 1 3 5 ^D 9 

但是,这个文件唾弃文件,所以它是不是一个好主意,使用大型文件。 请参阅j_random_hacker的答案 ,避免sl </s> 。

事先道歉反引号(“`”)的可读性,但这些工作在除了bash之外的shell中,因此更易于粘贴。 如果你使用一个接受它的shell,$(command …)格式比`command …`更具可读性(因此是可debugging的),所以你可以随意修改。

我有一个简单的函数在我的bashrc将使用awk来计算一些简单的math项目

 calc(){ awk 'BEGIN{print '$@' }' } 

这将做+, – ,*,/,^,%,sqrt,sin,cos,圆括号….(更多取决于你的awk版本)…你甚至可以用printf和格式化浮点输出,但这是我通常需要的

对于这个特定的问题,我只是简单地为每一行这样做:

 calc `echo $@|tr " " "+"` 

所以总结每行的代码块将如下所示:

 while read LINE || [ "$LINE" ]; do calc `echo $LINE|tr " " "+"` #you may want to filter out some lines with a case statement here done 

这就是如果你只想逐行求和他们。 但是对于数据文件中的每个数字

 VARS=`<datafile` calc `echo ${VARS// /+}` 

顺便说一句,如果我需要在桌面上快速做一些事情,我使用这个:

 xcalc() { A=`calc $@` A=`Xdialog --stdout --inputbox "Simple calculator" 0 0 $A` [ $A ] && xcalc $A } 

我已经对现有的答案做了一个快速的基准

  • 只使用标准的工具(抱歉像luarocket东西),
  • 是真正的单线,
  • 能够增加大量的数字(1亿),而且
  • 速度很快(我忽略了超过一分钟的时间)。

我总是在不到一分钟的时间内为我的机器添加了1到1亿的数字,

结果如下:

python

 :; seq 100000000 | python -c 'import sys; print sum(map(int, sys.stdin))' 5000000050000000 # 30s :; seq 100000000 | python -c 'import sys; print sum(int(s) for s in sys.stdin)' 5000000050000000 # 38s :; seq 100000000 | python3 -c 'import sys; print(sum(int(s) for s in sys.stdin))' 5000000050000000 # 27s :; seq 100000000 | python3 -c 'import sys; print(sum(map(int, sys.stdin)))' 5000000050000000 # 22s :; seq 100000000 | pypy -c 'import sys; print(sum(map(int, sys.stdin)))' 5000000050000000 # 11s :; seq 100000000 | pypy -c 'import sys; print(sum(int(s) for s in sys.stdin))' 5000000050000000 # 11s 

AWK

 :; seq 100000000 | awk '{s+=$1} END {print s}' 5000000050000000 # 22s 

粘贴&Bc

这在我的机器上耗尽了内存。 它工作的一半大小的input(5000万数字):

 :; seq 50000000 | paste -s -d+ - | bc 1250000025000000 # 17s :; seq 50000001 100000000 | paste -s -d+ - | bc 3750000025000000 # 18s 

所以我想这个1亿的数字大约要35秒。

Perl的

 :; seq 100000000 | perl -lne '$x += $_; END { print $x; }' 5000000050000000 # 15s :; seq 100000000 | perl -e 'map {$x += $_} <> and print $x' 5000000050000000 # 48s 

ruby

 :; seq 100000000 | ruby -e "puts ARGF.map(&:to_i).inject(&:+)" 5000000050000000 # 30s 

C

仅仅为了比较起见,我编译了C版本,并对其进行了testing,只是想知道基于工具的解决scheme有多慢。

 #include <stdio.h> int main(int argc, char** argv) { long sum = 0; long i = 0; while(scanf("%ld", &i) == 1) { sum = sum + i; } printf("%ld\n", sum); return 0; } 
 :; seq 100000000 | ./a.out 5000000050000000 # 8s 

结论

C当然是8s中最快的,但Pypy解决scheme只增加了30%到11%左右的开销 。 但是,公平地说,Pypy并不完全是标准的。 大多数人只安装了CPython,这个速度明显较慢(22s),与stream行的Awk解决scheme一样快。

基于标准工具的最快解决scheme是Perl(15s)。