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

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

作为背景知识,我有一个包含时序测量的日志文件。 通过grepping相关行和sed重新格式化,我可以列出该文件中的所有时间。 我想算出总数。 我可以将此中间输出通过管道传递给任何命令以进行最终求和。 我过去一直使用expr ,但是除非它在RPN模式下运行,否则我认为它无法应付(即使那样也很棘手)。

如何获得整数的总和?


#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

#2楼

球拍内衬:

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

#3楼

纯净的和短的打击。

f=$(cat numbers.txt)
echo $(( ${f//$'\n'/+} ))

#4楼

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)

#5楼

我意识到这是一个老问题,但是我很喜欢这个解决方案以分享它。

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

如果有兴趣,我将解释其运作方式。


#6楼

$ cat 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的答案 ,该答案可以避免


#7楼

我的十五美分:

$ 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

#8楼

实时求和可让您监视某些数字运算任务的进度。

$ cat numbers.txt 
1
2
3
4
5
6
7
8
9
10

$ cat numbers.txt | while read new; do total=$(($total + $new)); echo $total; done
1
3
6
10
15
21
28
36
45
55

(在这种情况下,无需将$total设置为零。完成后,您都无法访问$ total。)


#9楼

我的版本:

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

#10楼

我会对公认的解决方案大加警告:

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

#11楼

jq

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

#12楼

替代的纯Perl,相当易读,不需要软件包或选项:

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

#13楼

您可以使用首选的“ expr”命令,只需先稍微调整一下输入即可:

seq 10 | tr '[\n]' '+' | sed -e 's/+/ + /g' -e's/ + $/\n/' | xargs expr

该过程是:

  • “ tr”将eoln字符替换为+符号,
  • sed在两边用空格填充'+',然后从行中去除最后的+
  • xargs将管道输入插入命令行以供expr使用。

#14楼

对于红宝石爱好者

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

#15楼

perl -lne '$x += $_; END { print $x; }' < infile.txt

#16楼

应该做一点awk吗?

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

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

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

#17楼

如果您愿意的话,可以用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()))"

#18楼

以下是bash的工作原理:

I=0

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

echo $I

#19楼

Python的一线版:

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

#20楼

BASH解决方案,如果要将此命令用作命令(例如,如果需要经常执行此操作):

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

然后用法:

addnums < /tmp/nums

#21楼

普通打击:

$ 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

#22楼

以下应能工作(假设您的电话号码是每行的第二个字段)。

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

#23楼

您可以使用num-utils,尽管对于您所需的功能而言可能有些过分。 这是一组用于在Shell中处理数字的程序,可以做一些漂亮的事情,当然包括加起来。 有点过时了,但是它们仍然可以工作,如果您需要做更多的事情,可能会很有用。

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


#24楼

我认为AWK是您想要的:

awk '{sum+=$1}END{print sum}'

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


#25楼

粘贴通常会合并多个文件的行,但也可用于将文件的各个行转换为单个行。 分隔符标志允许您将x + x类型方程式传递给bc。

paste -s -d+ infile | bc

或者,当从标准输入进行配管时,

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

#26楼

dc -f infile -e '[+z1<r]srz1<rp'

请注意,带负号前缀的负数应转换为dc ,因为它使用_前缀而不是-前缀。 例如,通过tr '-' '_' | dc -f- -e '...' 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

作为伪代码:

  1. 将“ add_top_of_stack”定义为:
    1. 从堆栈中删除两个顶部值,然后将结果加回去
    2. 如果堆栈具有两个或多个值,请递归运行“ add_top_of_stack”
  2. 如果堆栈具有两个或多个值,请运行“ add_top_of_stack”
  3. 打印结果,现在是堆栈中剩下的唯一项目

为了真正理解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()

#27楼

sed 's/^/.+/' infile | bc | tail -1

#28楼

不能避免提交以下内容:

jot 1000000 | sed '2,$s/$/+/;$s/$/p/' | dc

在这里找到:
最优雅的unix shell单线求和列表的任意精度是多少?

与awk,bc和朋友相比,这是它的特殊优势:

  • 它不依赖于缓冲,因此不会因很大的输入而阻塞
  • 它表示没有特定的精度-或该问题的整数大小-限制
  • 如果需要添加浮点数,则无需其他代码

#29楼

我对现有答案做了一个快速基准测试

  • 仅使用标准工具(对不起luarocket类的东西),
  • 是真正的一线客
  • 有能力增加大量的数字(1亿),并且
  • 速度很快(我忽略了花费一分钟以上的时间)。

我总是将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

Awk

:; 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

为了比较起见,我编译了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)。


#30楼

普通打击一线

$ cat > /tmp/test
1 
2 
3 
4 
5
^D

$ echo $(( $(cat /tmp/test | tr "\n" "+" ) 0 ))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值