首页 文章

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

提问于
浏览
717

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

作为一个背景,我有一个日志文件,其中包括时序测量,所以通过grepping相关的行,以及一些 sed 重新格式化我可以列出该文件中的所有时间 . 我'd like to work out the total however, and my mind has gone blank as to any command I can pipe this intermediate output to in order to do the final sum. I'过去总是使用 expr ,但除非它在 RPN mode 中运行,否则我不会应对这个问题(即便如此,它也会很棘手) .

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

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


Update :哇,正如所料,这里有一些不错的答案 . 看起来我一定要把 awk 更深入的检查作为 command-line tool 一般!

30 回答

  • 10

    使用jq

    seq 10 | jq -s 'add' # 'add' is equivalent to 'reduce .[] as $item (0; . + $item)'
    
  • 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
    
  • 592

    Racket中的单线程:

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

    普通bash一个衬垫

    $ cat > /tmp/test
    1 
    2 
    3 
    4 
    5
    ^D
    
    $ echo $(( $(cat /tmp/test | tr "\n" "+" ) 0 ))
    
  • 11

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

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

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

  • 107

    Python中的单行版本:

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

    你可以在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()))"
    
  • 69

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

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

    如果有兴趣,我会解释它是如何工作的 .

  • 3

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

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

  • 8

    纯粹和短暂的bash .

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

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

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

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

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

    以下在bash中有效:

    I=0
    
    for N in `cat numbers.txt`
    do
        I=`expr $I + $N`
    done
    
    echo $I
    
  • 9
    sed 's/^/.+/' infile | bc | tail -1
    
  • 2

    我的版本:

    seq -5 10 | xargs printf "- - %s" | xargs  | bc
    
  • 24
    $ 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,这可以避免啜饮 .

  • 2

    纯粹的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
    
  • 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)
    
  • 67

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

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

    过程是:

    • "tr"用符号替换eoln字符,

    • sed填充'+',每侧有空格,然后从线上剥去最后一个

    • xargs将管道输入插入到命令行中以供expr使用 .

  • 47

    C(简化):

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

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

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

  • 6

    对于Ruby Lovers

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

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

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

  • 6

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

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

    然后用法:

    addnums < /tmp/nums
    
  • 36

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

    paste -s -d+ infile | bc
    

    或者,当从stdin管道时,

    <commands> | paste -s -d+ - | bc
    
  • 1121
    perl -lne '$x += $_; END { print $x; }' < infile.txt
    
  • 3

    有点awk应该这样做吗?

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

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

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

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

    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
    

    附:我本来想评论第一个答案,但我没有足够的声誉..

  • 13

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

    • 只使用标准工具(抱歉 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
    

    粘贴&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
    

    Ruby

    :; 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,但 the Pypy solution only adds a very little overhead of about 30% to 11s . 但是,公平地说,Pypy并不完全是标准的 . 大多数人只安装了CPython,速度明显慢了(22s),与流行的Awk解决方案一样快 .

    The fastest solution based on standard tools is Perl (15s).

  • 15

    普通狂欢:

    $ 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
    
  • 2
    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
    

    作为伪代码:

    • 将"add_top_of_stack"定义为:

    • 从堆栈中删除两个顶部值并将结果添加回来

    • 如果堆栈有两个或更多值,则递归运行"add_top_of_stack"

    • 如果堆栈有两个或更多值,请运行"add_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()
    

相关问题