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

时间:2009-01-16 15:42:04

标签: shell

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

作为一个背景知识,我有一个包含时序测量的日志文件,因此通过对相关行进行grepping,以及一些sed重新格式化,我可以列出该文件中的所有时序。然而,我想计算出总数,而且我的思绪已经变得空白,因为任何命令我都可以将这个中间输出传递到最终总和。我过去总是使用expr,但除非它在RPN模式下运行,否则我认为它不会应付这个问题(即便如此,它也会很棘手)。

我错过了什么?鉴于可能有多种方法可以实现这一点,我将很乐意阅读(并提升)任何有效的方法,即使其他人已经发布了一个不同的解决方案来完成这项工作。

相关问题:Shortest command to calculate the sum of a column of output on Unix? (信用@Andrew


更新:哇,正如所料,这里有一些不错的答案。看起来我一定要把awk作为命令行工具进行更深入的检查!

43 个答案:

答案 0 :(得分:1193)

有点awk应该这样做吗?

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

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

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

答案 1 :(得分:620)

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

paste -s -d+ infile | bc

或者,当从stdin管道时,

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

答案 2 :(得分:112)

Python中的单行版本:

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

答案 3 :(得分:77)

我会对通常认可的解决方案提出一个很大的警告:

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

答案 4 :(得分:73)

普通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

答案 5 :(得分:63)

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

请注意,前缀为减号的负数应翻译为dc,因为它使用_前缀而不是-前缀。例如,通过tr '-' '_' | dc -f- -e '...'

编辑:由于这个答案得到了很多“默默无闻”的投票,这里有一个详细的解释:

表达式[+z1<r]srz1<rp does the following

[   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. 打印结果,现在是堆栈中剩下的唯一项目
  4. 要真正理解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()
    

答案 6 :(得分:51)

使用jq

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

答案 7 :(得分:45)

纯粹和短暂的打击。

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

答案 8 :(得分:36)

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

答案 9 :(得分:25)

我十五美分:

$ 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

答案 10 :(得分:18)

我已经对现有答案进行了快速基准测试

  • 仅使用标准工具(抱歉luarocket),
  • 是真正的单行,
  • 能够添加大量数字(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

粘贴&amp; BC

我的机器内存不足。它的工作量只有输入的一半(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

红宝石

:; 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解决方案只增加了大约30%到11s 的非常小的开销。但是,公平地说,Pypy并不完全是标准的。大多数人只安装了CPython,速度要慢得多(22秒),与流行的Awk解决方案一样快。

基于标准工具的最快解决方案是Perl(15秒)。

答案 11 :(得分:17)

BASH解决方案,如果你想把它作为一个命令(例如,如果你需要经常这样做):

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

然后用法:

addnums < /tmp/nums

答案 12 :(得分:16)

普通bash one liner

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

$ echo $(( $(cat /tmp/test | tr "\n" "+" ) 0 ))

答案 13 :(得分:11)

你可以使用num-utils,虽然它可能对你需要的东西有点过分。这是一组用于操作shell中的数字的程序,可以做几个很好的事情,当然包括添加它们。它有点过时了,但是如果你需要做更多的事情,它们仍然可以工作并且非常有用。

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

答案 14 :(得分:11)

以下适用于bash:

I=0

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

echo $I

答案 15 :(得分:10)

我认为AWK正是您所寻找的:

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

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

答案 16 :(得分:9)

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

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

如果有兴趣,我会解释它是如何运作的。

答案 17 :(得分:9)

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

答案 18 :(得分:8)

纯粹的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

答案 19 :(得分:6)

替代纯Perl,相当可读,不需要包或选项:

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

答案 20 :(得分:6)

对于Ruby Lovers

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

答案 21 :(得分:4)

我的版本:

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

答案 22 :(得分:3)

无法避免提交:

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

在这里找到:
Most elegant unix shell one-liner to sum list of numbers of arbitrary precision?

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

  • 它不依赖于缓冲,因此不会被非常大的输入阻塞
  • 它意味着没有特定的精度 - 或整数大小的那个问题 - 限制
  • 如果需要添加浮点数,则不需要不同的代码

答案 23 :(得分:3)

如果你觉得舒服,可以在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()))"

答案 24 :(得分:3)

以下内容应该有效(假设您的号码是每行的第二个字段)。

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

答案 25 :(得分:3)

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)

答案 26 :(得分:3)

Racket中的单行:

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

答案 27 :(得分:2)

您可以使用首选的'expr'命令,您只需先输入一点输入:

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

过程是:

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

答案 28 :(得分:2)

提前为反复可读性(“”“)的可读性道歉,但这些在除了bash之外的shell中起作用,因此更易于粘贴。如果你使用一个接受它的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
}

答案 29 :(得分:2)

实时汇总,让您监控一些数字运算任务的进度。

$ 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。)

答案 30 :(得分:2)

C ++(简化):

echo {1..10} | scc 'WRL n+=$0; n'

SCC项目 - http://volnitsky.com/project/scc/

SCC是shell提示符下的C ++片段评估程序

答案 31 :(得分:2)

$ 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's answer,以避免诽谤。

答案 32 :(得分:1)

使用env变量tmp

tmp=awk -v tmp="$tmp" '{print $tmp" "$1}' <filename>|echo $tmp|sed "s/ /+/g"|bc

tmp=cat <filename>|awk -v tmp="$tmp" '{print $tmp" "$1}'|echo $tmp|sed "s/ /+/g"|bc

感谢。

答案 33 :(得分:1)

您可以使用Alacon - Alasql数据库的命令行实用程序。

它适用于Node.js,因此您需要安装Node.js然后Alasql包:

要从stdin计算总和,您可以使用以下命令:

> cat data.txt | node alacon "SELECT VALUE SUM([0]) FROM TXT()" >b.txt

答案 34 :(得分:1)

为了完整起见,还有一个R解决方案

seq 1 10 | R -q -e "f <- file('stdin'); open(f); cat(sum(as.numeric(readLines(f))))"

答案 35 :(得分:1)

lua解释器出现在所有基于fedora的系统上[fedora,RHEL,CentOS,korora等,因为它嵌入了rpm-package(包管理器rpm包),即rpm-lua],如果你想要的话学习lua这种问题是理想的(你也可以完成你的工作)。

cat filname | lua -e "sum = 0;for i in io.lines() do sum=sum+i end print(sum)"

它有效。虽然Lua很冗长,你可能不得不忍受一些重复的键盘中风伤害:)

答案 36 :(得分:0)

使用GNU datamash util

#include <algorithm>

输出:

seq 10 | datamash sum 1

答案 37 :(得分:0)

...和PHP版本,只是为了完整性

cat /file/with/numbers | php -r '$s = 0; while (true) { $e = fgets(STDIN); if (false === $e) break; $s += $e; } echo $s;'

答案 38 :(得分:0)

一个简单的解决方案是编写一个程序来为您完成。这可能在python中很快完成,例如:

sum = 0
file = open("numbers.txt","R")
for line in file.readlines(): sum+=int(line)
file.close()
print sum

我没有测试过该代码,但它看起来是正确的。只需将numbers.txt更改为文件名,将代码保存到名为sum.py的文件中,并在控制台中键入“python sum.py”

答案 39 :(得分:0)

Rebol中的单行:

rebol -q --do 's: 0 while [d: input] [s: s + to-integer d] print s' < infile.txt

不幸的是,上面的内容在Rebol 3中不起作用( INPUT 不会传输STDIN)。

所以这是一个临时解决方案,也适用于Rebol 3:

rebol -q --do 's: 0 foreach n to-block read %infile.txt [s: s + n] print s'

答案 40 :(得分:0)

#include <iostream>

int main()
{
    double x = 0, total = 0;
    while (std::cin >> x)
        total += x;
    if (!std::cin.eof())
        return 1;
    std::cout << x << '\n';
}

答案 41 :(得分:-1)

简单的php

  cat numbers.txt | php -r "echo array_sum(explode(PHP_EOL, stream_get_contents(STDIN)));"

答案 42 :(得分:-5)

好的,这是在PowerShell(PowerShell核心,应在Windows,Linux和Mac上运行)上的实现方法

Get-Content aaa.dat | Measure-Object -Sum