我正在寻找一个命令,它将接受多行文本作为输入,每行包含一个整数,并输出这些整数的总和。
背景说明:我有一个日志文件,其中包括时间测量。通过grep相关行和一些sed
重新格式化,我可以列出该文件中的所有计时。我想计算出总时间。我可以将此中间输出传输到任何命令以进行最终求和。我过去总是使用expr
,但是除非它以RPN模式运行,否则我认为它无法处理这个问题(即使是在RPN模式下也很棘手)。
如何获得整数的总和?
我正在寻找一个命令,它将接受多行文本作为输入,每行包含一个整数,并输出这些整数的总和。
背景说明:我有一个日志文件,其中包括时间测量。通过grep相关行和一些sed
重新格式化,我可以列出该文件中的所有计时。我想计算出总时间。我可以将此中间输出传输到任何命令以进行最终求和。我过去总是使用expr
,但是除非它以RPN模式运行,否则我认为它无法处理这个问题(即使是在RPN模式下也很棘手)。
如何获得整数的总和?
我对现有答案进行了快速基准测试,这些答案:
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
:; seq 100000000 | awk '{s+=$1} END {print s}'
5000000050000000
# 22s
在我的电脑上,这个程序因内存不足而崩溃。它只能处理一半大小的输入(5000万个数字):
:; seq 50000000 | paste -s -d+ - | bc
1250000025000000
# 17s
:; seq 50000001 100000000 | paste -s -d+ - | bc
3750000025000000
# 18s
所以我猜测生成1亿个数字需要大约35秒时间。
:; seq 100000000 | perl -lne '$x += $_; END { print $x; }'
5000000050000000
# 15s
:; seq 100000000 | perl -e 'map {$x += $_} <> and print $x'
5000000050000000
# 48s
:; seq 100000000 | ruby -e "puts ARGF.map(&:to_i).inject(&:+)"
5000000050000000
# 30s
出于比较的目的,我还编译了 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;
}
:; seq 100000000 | ./a.out
5000000050000000
# 8s
C语言是最快的,只需8秒,但Pypy解决方案仅增加了约30%的额外开销,达到了11秒。但是,公平地说,Pypy并不完全标准化。大多数人只安装了CPython,其速度慢得多(22秒),与流行的Awk解决方案一样快。
基于标准工具的最快解决方案是Perl(15秒)。
use std::io::{self, BufRead};
fn main() {
let stdin = io::stdin();
let mut sum: i64 = 0;
for line in stdin.lock().lines() {
sum += line.unwrap().parse::().unwrap();
}
println!("{}", sum);
}
- Jocelyn$0
而不是$1
,awk也可以匹配perl。 - Amit Naiduseq 10 | datamash sum 1
输出:
55
如果输入数据不规则,有空格和制表符在奇怪的位置,这可能会使datamash
混淆,那么可以使用-W
开关:<commands...> | datamash -W sum 1
...或者使用tr
来清除空格:
<commands...> | tr -d '[[:blank:]]' | datamash sum 1
如果输入足够大,输出将会使用科学计数法。seq 100000000 | datamash sum 1
输出:
5.00000005e+15
要将它转换为十进制,请使用 --format
选项:
seq 100000000 | datamash --format '%.0f' sum 1
输出:
5000000050000000
简单的Bash一行代码
$ cat > /tmp/test
1
2
3
4
5
^D
$ echo $(( $(cat /tmp/test | tr "\n" "+" ) 0 ))
echo $(( $( tr "\n" "+" < /tmp/test) 0 ))
пјҲжіЁпјҡиҝҷжҳҜдёҖдёӘLinuxе‘Ҫд»ӨпјҢз”ЁдәҺе°Ҷж–Ү件дёӯжҜҸиЎҢзҡ„ж•°еӯ—зӣёеҠ 并иҫ“еҮәз»“жһңгҖӮпјү - agctr
并不完全是“纯粹的 Bash”/吹毛求疵 - Benjamin W.BASH的解决方案,如果您想将此作为命令使用(例如,如果您需要频繁执行此操作):
addnums () {
local total=0
while read val; do
(( total += val ))
done
echo $total
}
然后的用法:
addnums < /tmp/nums
你可以使用num-utils,虽然它可能会过于强大,超出你的需要范畴。这是一组用于在shell中操作数字的程序,并且可以执行几个聪明的操作,包括当然是对它们进行加法运算。这个程序有点过时了,但它们仍然有效,如果你需要做更多的事情,它们可能会很有用。
https://suso.suso.org/programs/num-utils/index.phtml
它非常简单易用:
$ seq 10 | numsum
55
但是对于大输入会耗尽内存。
$ seq 100000000 | numsum
Terminado (killed)
printf "%s\n" 1 3 5 | numsum
- Jan Matějka无法避免提交此内容,这是对该问题最常见的方法,请查看:
jot 1000000 | sed '2,$s/$/+/;$s/$/p/' | dc
这里可以找到它,我是 OP,答案来自观众:
以下是它相对于 awk、bc、perl、GNU 的 datamash 等的特殊优势:
jot
测试,即使对于相当大的列表,速度也非常合理。另外,如果将datamash作为OP问题的解决方案,则任何编译的汇编程序也应该是可以接受的...这将加快速度! - fgeorgatostime seq 10000000 | sed '2,$s/$/+/;$s/$/p/' | dc
计算结果正确,用时43秒,而 time seq 10000000 | datamash sum 1
只需1秒钟即可完成,速度超过40倍。此外,正如您所说的“编译汇编程序”,可能是一个更加复杂的解决方案,速度也不会更快,并且更有可能给出错误的解决方案。 - Gabriel Ravier我知道这是一个老问题,但我喜欢这个解决方案足够分享它。
% cat > numbers.txt
1
2
3
4
5
^D
% cat numbers.txt | perl -lpe '$c+=$_}{$_=$c'
15
如果有兴趣,我会解释它的工作原理。
I=0
for N in `cat numbers.txt`
do
I=`expr $I + $N`
done
echo $I
cat numbers.txt
步骤将会有问题。 - Giacomosed 's/^/.+/' infile | bc | tail -1
$ 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
((
括号 ))
? - Atcold$(< numbers.txt)
,使其成为纯bash。 - Dani_l