壳命令求和整数,每行一个?
我正在寻找一个命令,将接受作为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
作为伪代码:
- 将“add_top_of_stack”定义为:
- 从堆栈中移除两个最高值并将结果添加回去
- 如果堆栈有两个或多个值,请recursion运行“add_top_of_stack”
- 如果堆栈有两个或更多的值,运行“add_top_of_stack”
- 打印结果,现在是堆栈中剩下的唯一项目
要真正理解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中操纵数字的程序,可以做几件漂亮的事情,当然包括添加它们。 这有点过时了,但是如果你需要做更多的事情,它们还是可以工作的。
我意识到这是一个古老的问题,但我喜欢这个解决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 }
我已经对现有的答案做了一个快速的基准
- 只使用标准的工具(抱歉像
lua
或rocket
东西), - 是真正的单线,
- 能够增加大量的数字(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)。