我正在寻找一个命令,该命令将接受(作为输入)多行文本,每行包含一个整数,并输出这些整数的总和。
作为背景知识,我有一个包含时序测量的日志文件。通过grepping相关行和sed
重新格式化,我可以列出该文件中的所有时间。我想算出总数。我可以将此中间输出通过管道传递给任何命令,以进行最终求和。我过去一直使用expr
过,但是除非它以RPN模式运行,否则我认为它不能解决这个问题(即使那样也很棘手)。
如何获得整数的总和?
我正在寻找一个命令,该命令将接受(作为输入)多行文本,每行包含一个整数,并输出这些整数的总和。
作为背景知识,我有一个包含时序测量的日志文件。通过grepping相关行和sed
重新格式化,我可以列出该文件中的所有时间。我想算出总数。我可以将此中间输出通过管道传递给任何命令,以进行最终求和。我过去一直使用expr
过,但是除非它以RPN模式运行,否则我认为它不能解决这个问题(即使那样也很棘手)。
如何获得整数的总和?
Answers:
应该做一点awk吗?
awk '{s+=$1} END {print s}' mydatafile
注意:如果要添加超过2 ^ 31(2147483647)的任何内容,某些版本的awk会有一些奇怪的行为。有关更多背景,请参见评论。一种建议是使用printf
而不是print
:
awk '{s+=$1} END {printf "%.0f", s}' mydatafile
ls $@ | xargs -i pdftk {} dump_data | grep NumberOfPages | awk '{s+=$2} END {print s}'
awk '{s+=$1} END {printf "%.0f", s}' mydatafile
代替。
粘贴通常会合并多个文件的行,但也可以用于将文件的各个行转换为单个行。分隔符标志允许您将x + x类型方程式传递给bc。
paste -s -d+ infile | bc
或者,当从标准输入进行配管时,
<commands> | paste -s -d+ - | bc
paste
可以使用破折号-
作为文件名-这将允许您将命令输出中的数字通过管道传递到粘贴的标准输出中,而无需先创建文件:<commands> | paste -sd+ - | bc
-
。(如果您想将文件与 stdin 结合使用,则很有用)。
Python的一线版:
$ python -c "import sys; print(sum(int(l) for l in sys.stdin))"
python -c"import sys; print(sum(map(int, sys.stdin)))"
find . -name '*.epub' -exec stat -c %s '{}' \; | python -c "import sys; nums = [int(n) for n in sys.stdin if int(n) < 10000000]; print(sum(nums)/len(nums))"
import sys; print(sum(int(''.join(c for c in l if c.isdigit())) for l in sys.stdin))
我会对公认的解决方案大加警告:
awk '{s+=$1} END {print s}' mydatafile # DO NOT USE THIS!!
这是因为awk使用这种形式使用32位有符号整数表示形式:如果总和超过2147483647(即2 ^ 31),它将溢出。
一个更通用的答案(用于求和整数)将是:
awk '{s+=$1} END {printf "%.0f\n", s}' mydatafile # USE THIS INSTEAD
echo -e "2147483647 \n 100" |awk '{s+=$1}END{print s}'
显示2147483747
echo 999999999999999999 | awk '{s+=$1} END {printf "%.0f\n", s}'
生产1000000000000000000
普通打击:
$ 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
num
定义?我相信它与< numbers.txt
表达方式有某种联系,但目前尚不清楚。
dc -f infile -e '[+z1<r]srz1<rp'
请注意,应将带有负号前缀的负数转换为dc
,因为它使用_
前缀而不是-
前缀。例如,通过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
作为伪代码:
为了真正理解的简单性和功能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()
(echo "0"; sed 's/$/ +/' inp; echo 'pq')|dc
。
dc -e '0 0 [+?z1<m]dsmxp'
。因此,我们不会在处理之前将所有数字保存在堆栈中,而是一一读取和处理它们(更精确地说,一行一行,因为一行可以包含多个数字)。请注意,空行可以终止输入序列。
sed
替换中的空格可以删除,因为dc
它并不关心参数和运算符之间的空格。(echo "0"; sed 's/$/+/' inputFile; echo 'pq')|dc
纯净的和短的打击。
f=$(cat numbers.txt)
echo $(( ${f//$'\n'/+} ))
f=$(<numbers.txt)
。
f=$(cat); echo $(( ${f//$'\n'/+} ))
脚本,则可以将任何内容通过管道传递到该脚本,也可以调用不带参数的交互式stdin输入(以Control-D终止)。
<numbers.txt
是一个改进,但是,总的来说,此解决方案仅对小型输入文件有效。例如,使用1,000条输入行的文件,awk
在我的计算机上可接受的解决方案的速度大约是20倍-并且还消耗更少的内存,因为该文件不会一次全部读取。
perl -lne '$x += $_; END { print $x; }' < infile.txt
我的十五美分:
$ 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
grep -v '^$'
。谢谢!
我对现有答案做了一个快速基准测试
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
这用完了我机器上的内存。它的工作量仅为输入量(5,000万个数字)的一半:
:; seq 50000000 | paste -s -d+ - | bc
1250000025000000
# 17s
:; seq 50000001 100000000 | paste -s -d+ - | bc
3750000025000000
# 18s
因此,我想一亿个数字大约需要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的速度当然是8s最快,但是Pypy解决方案只增加了很少的开销,约为11s的30%。但是,公平地说,Pypy并非完全标准。大多数人只安装了CPython,速度明显慢(22秒),与流行的Awk解决方案一样快。
基于标准工具的最快解决方案是Perl(15s)。
paste
+ bc
的办法正是我一直在寻找到和十六进制值,谢谢!
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::<i64>().unwrap(); } println!("{}", sum); }
以下是bash的工作原理:
I=0
for N in `cat numbers.txt`
do
I=`expr $I + $N`
done
echo $I
cat numbers.txt
步骤将成问题。
您可以使用num-utils,尽管对于您所需的功能而言可能有些过分。这是一组用于在Shell中处理数字的程序,可以做一些漂亮的事情,包括将它们加起来。有点过时了,但是它们仍然可以工作,如果您需要做更多的事情,可能会很有用。
numsum numbers.txt
。
我意识到这是一个老问题,但是我很喜欢这个解决方案以分享它。
% cat > numbers.txt
1
2
3
4
5
^D
% cat numbers.txt | perl -lpe '$c+=$_}{$_=$c'
15
如果有兴趣,我将解释其运作方式。
替代的纯Perl,相当易读,不需要软件包或选项:
perl -e "map {$x += $_} <> and print $x" < infile.txt
不能避免提交以下内容:
jot 1000000 | sed '2,$s/$/+/;$s/$/p/' | dc
在这里可以找到:
最优雅的unix shell单线求和任意精度的数字列表?
与awk,bc和朋友相比,这是它的特殊优势:
seq 10 | datamash sum 1
输出:
55
如果输入数据不规则,并且空格和制表符放在奇数个位置,这可能会造成混淆datamash
,则可以使用以下-W
开关:
<commands...> | datamash -W sum 1
...或用于tr
清理空白:
<commands...> | tr -d '[[:blank:]]' | datamash sum 1
如果您愿意的话,可以用python来做:
未经测试,只需输入:
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()))"
cat
用于演示该脚本适用于stdin和argv []中的文件(如while(<>)
Perl)。如果您的输入在文件中,则不需要'<'。
< numbers.txt
证明它在stdin上也能正常cat numbers.txt |
工作。而且它不会教坏习惯。
$猫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的答案,该答案可以避免咽。
以下应能工作(假设您的电话号码是每行的第二个字段)。
awk 'BEGIN {sum=0} \
{sum=sum + $2} \
END {print "tot:", sum}' Yourinputfile.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)
预先道歉以提高反引号(“`”)的可读性,但是这些词法在除bash之外的其他shell中起作用,因此更易于粘贴。如果您使用接受它的外壳,则$(command ...)格式比“ command ...”更具可读性(因此可调试),因此可以随意进行修改。
我的bashrc中有一个简单的函数,它将使用awk计算许多简单的数学项
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
}
$()
?