我正在寻找一个命令,它将接受多行文本作为输入,每行包含一个整数,并输出这些整数的总和.
作为一个背景,我有一个日志文件,其中包括时序测量,所以通过grepping相关的行,并稍微sed
重新格式化,我可以列出该文件中的所有时间.然而,我想计算出总数,而且我的思绪已经变得空白,因为任何命令我都可以将这个中间输出传递到最终总和.我过去总是使用expr
它,但除非它运行在sed
我认为它不会应付这个(即使那样它也会很棘手).
我错过了什么?鉴于可能有多种方法可以实现这一点,我将很乐意阅读(和expr
)任何有效的方法,即使其他人已经发布了一个不同的解决方案来完成这项工作.
相关问题:在Unix上计算一列输出总和的最短命令?(来自@Andrew)
更新:哇,正如所料,这里有一些不错的答案.看起来我一定要进行sed
更深入的检查expr
!
Pau*_*xon 1241
有点awk应该这样做吗?
awk '{s+=$1} END {print s}' mydatafile
Run Code Online (Sandbox Code Playgroud)
注意:如果您要添加超过2 ^ 31(2147483647)的任何内容,某些版本的awk会有一些奇怪的行为.有关更多背景,请参阅评论 一个建议是使用printf
而不是print
:
awk '{s+=$1} END {printf "%.0f", s}' mydatafile
Run Code Online (Sandbox Code Playgroud)
小智 643
粘贴通常合并多个文件的行,但它也可用于将文件的各行转换为单行.分隔符标志允许您将x + x类型方程传递给bc.
paste -s -d+ infile | bc
Run Code Online (Sandbox Code Playgroud)
或者,当从stdin管道时,
<commands> | paste -s -d+ - | bc
Run Code Online (Sandbox Code Playgroud)
dF.*_*dF. 122
Python中的单行版本:
$ python -c "import sys; print(sum(int(l) for l in sys.stdin))"
Run Code Online (Sandbox Code Playgroud)
Gia*_*lli 80
我会对通常认可的解决方案提出一个很大的警告:
awk '{s+=$1} END {print s}' mydatafile # DO NOT USE THIS!!
Run Code Online (Sandbox Code Playgroud)
这是因为在这种形式下,awk使用32位有符号整数表示:对于超过2147483647(即2 ^ 31)的和,它将溢出.
更一般的答案(对于求和整数)将是:
awk '{s+=$1} END {printf "%.0f\n", s}' mydatafile # USE THIS INSTEAD
Run Code Online (Sandbox Code Playgroud)
PS我本来想评论第一个答案,但我没有足够的声誉..
Gia*_*omo 74
普通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
Run Code Online (Sandbox Code Playgroud)
CB *_*ley 63
dc -f infile -e '[+z1<r]srz1<rp'
Run Code Online (Sandbox Code Playgroud)
请注意,前缀为减号的负数应该被翻译dc
,因为它使用_
前缀而不是-
前缀.例如,via tr '-' '_' | dc -f- -e '...'
.
表达式[+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
Run Code Online (Sandbox Code Playgroud)
作为伪代码:
为了真正理解它的简单性和强大功能dc
,这里有一个有效的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()
Run Code Online (Sandbox Code Playgroud)
ban*_*yan 56
用jq:
seq 10 | jq -s 'add' # 'add' is equivalent to 'reduce .[] as $item (0; . + $item)'
Run Code Online (Sandbox Code Playgroud)
小智 46
纯粹和短暂的bash.
f=$(cat numbers.txt)
echo $(( ${f//$'\n'/+} ))
Run Code Online (Sandbox Code Playgroud)
j_r*_*ker 36
perl -lne '$x += $_; END { print $x; }' < infile.txt
Run Code Online (Sandbox Code Playgroud)
inn*_*rld 28
我十五美分:
$ cat file.txt | xargs | sed -e 's/\ /+/g' | bc
Run Code Online (Sandbox Code Playgroud)
例:
$ 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
Run Code Online (Sandbox Code Playgroud)
Alf*_*lfe 20
我已经对现有答案做了快速基准测试
lua
或类似的东西rocket
),对于多种解决方案,我总是在不到一分钟的时间内添加了1到1亿的数字,这在我的机器上是可行的.
结果如下:
:; 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
Run Code Online (Sandbox Code Playgroud)
:; seq 100000000 | awk '{s+=$1} END {print s}'
5000000050000000
# 22s
Run Code Online (Sandbox Code Playgroud)
这在我的机器上耗尽了内存.它的工作量只有输入的一半(5000万个数字):
:; seq 50000000 | paste -s -d+ - | bc
1250000025000000
# 17s
:; seq 50000001 100000000 | paste -s -d+ - | bc
3750000025000000
# 18s
Run Code Online (Sandbox Code Playgroud)
所以我想这对于1亿个数字来说需要大约35秒.
:; seq 100000000 | perl -lne '$x += $_; END { print $x; }'
5000000050000000
# 15s
:; seq 100000000 | perl -e 'map {$x += $_} <> and print $x'
5000000050000000
# 48s
Run Code Online (Sandbox Code Playgroud)
:; seq 100000000 | ruby -e "puts ARGF.map(&:to_i).inject(&:+)"
5000000050000000
# 30s
Run Code Online (Sandbox Code Playgroud)
仅仅为了比较,我编译了C版本并对其进行了测试,只是想知道基于工具的解决方案有多慢.
#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;
}
Run Code Online (Sandbox Code Playgroud)
:; seq 100000000 | ./a.out
5000000050000000
# 8s
Run Code Online (Sandbox Code Playgroud)
C当然是最快的8s,但Pypy解决方案只增加了大约30%到11s的非常小的开销.但是,公平地说,Pypy并不完全是标准的.大多数人只安装了CPython,速度要慢得多(22秒),与流行的Awk解决方案一样快.
基于标准工具的最快解决方案是Perl(15s).
Jay*_*Jay 17
BASH解决方案,如果你想让它成为一个命令(例如,如果你需要经常这样做):
addnums () {
local total=0
while read val; do
(( total += val ))
done
echo $total
}
Run Code Online (Sandbox Code Playgroud)
然后用法:
addnums < /tmp/nums
Run Code Online (Sandbox Code Playgroud)
Kha*_*din 16
普通bash一个衬垫
$ cat > /tmp/test
1
2
3
4
5
^D
$ echo $(( $(cat /tmp/test | tr "\n" "+" ) 0 ))
Run Code Online (Sandbox Code Playgroud)
Pao*_*olo 12
我认为AWK正是您所寻找的:
awk '{sum+=$1}END{print sum}'
Run Code Online (Sandbox Code Playgroud)
您可以通过将数字列表传递到标准输入或将包含数字的文件作为参数传递来使用此命令.
Fra*_*edo 11
以下在bash中有效:
I=0
for N in `cat numbers.txt`
do
I=`expr $I + $N`
done
echo $I
Run Code Online (Sandbox Code Playgroud)
syk*_*ora 11
你可以使用num-utils,虽然它可能对你需要的东西有点过分.这是一组用于操作shell中的数字的程序,可以做几个很好的事情,当然包括添加它们.它有点过时,但它们仍然有用,如果你需要做更多的事情,它们会很有用.
http://suso.suso.org/programs/num-utils/
agc*_*agc 11
seq 10 | datamash sum 1
Run Code Online (Sandbox Code Playgroud)
输出:
55
Run Code Online (Sandbox Code Playgroud)
如果输入数据不规则,在奇数位置有空格和制表符,这可能会混淆datamash
,然后使用-W
开关:
<commands...> | datamash -W sum 1
Run Code Online (Sandbox Code Playgroud)
...或用于tr
清理空白:
<commands...> | tr -d '[[:blank:]]' | datamash sum 1
Run Code Online (Sandbox Code Playgroud)
如果输入足够大,输出将采用科学计数法。
seq 100000000 | datamash sum 1
Run Code Online (Sandbox Code Playgroud)
输出:
5.00000005e+15
Run Code Online (Sandbox Code Playgroud)
要将其转换为十进制,请使用以下--format
选项:
seq 100000000 | datamash --format '%.0f' sum 1
Run Code Online (Sandbox Code Playgroud)
输出:
5000000050000000
Run Code Online (Sandbox Code Playgroud)
我意识到这是一个老问题,但我喜欢这个解决方案足以分享它.
% cat > numbers.txt
1
2
3
4
5
^D
% cat numbers.txt | perl -lpe '$c+=$_}{$_=$c'
15
Run Code Online (Sandbox Code Playgroud)
如果有兴趣,我会解释它是如何工作的.
小智 9
纯粹的bash和单线:-)
$ 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
Run Code Online (Sandbox Code Playgroud)
无法避免提交这个,这是这个问题最通用的方法,请检查:
jot 1000000 | sed '2,$s/$/+/;$s/$/p/' | dc
Run Code Online (Sandbox Code Playgroud)
在这里可以找到,我是OP,答案来自观众:
这是它相对于awk、bc和朋友的特殊优势:
小智 6
替代纯Perl,相当可读,不需要包或选项:
perl -e "map {$x += $_} <> and print $x" < infile.txt
Run Code Online (Sandbox Code Playgroud)
对于Ruby Lovers
ruby -e "puts ARGF.map(&:to_i).inject(&:+)" numbers.txt
Run Code Online (Sandbox Code Playgroud)
这是一段漂亮、干净的Raku(以前称为 Perl 6)一行:
\nsay [+] slurp.lines\n
Run Code Online (Sandbox Code Playgroud)\n我们可以像这样使用它:
\n% seq 10 | raku -e "say [+] slurp.lines"\n55\n
Run Code Online (Sandbox Code Playgroud)\n它的工作原理如下:
\nslurp
不带任何参数默认从标准输入读取;它返回一个字符串。对字符串调用该lines
方法会返回该字符串的行列表。
周围的括号+
变成+
一个归约元运算符,它将列表减少为单个值:列表中值的总和。say
然后用换行符将其打印到标准输出。
需要注意的一件事是,我们从来没有显式地将行转换为数字\xe2\x80\x94Raku 足够聪明,可以为我们做到这一点。然而,这意味着我们的代码在输入绝对不是数字时中断:
\n% echo "1\\n2\\nnot a number" | raku -e "say [+] slurp.lines"\nCannot convert string to number: base-10 number must begin with valid digits or \'.\' in \'\xe2\x8f\x8fnot a number\' (indicated by \xe2\x8f\x8f)\n in block <unit> at -e line 1\n
Run Code Online (Sandbox Code Playgroud)\n