逐行相加整数的Shell命令?

1083

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

背景说明:我有一个日志文件,其中包括时间测量。通过grep相关行和一些sed重新格式化,我可以列出该文件中的所有计时。我想计算出总时间。我可以将此中间输出传输到任何命令以进行最终求和。我过去总是使用expr,但是除非它以RPN模式运行,否则我认为它无法处理这个问题(即使是在RPN模式下也很棘手)。

如何获得整数的总和?


2
这与我之前问过的一个问题非常相似:https://dev59.com/OHVC5IYBdhLWcg3wbglT - An̲̳̳drew
这个问题感觉像是一个代码高尔夫的问题。https://codegolf.stackexchange.com/ :) - Gordon Bean
47个回答

6

给喜爱Ruby的人

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

6

另一种纯Perl的方法,相当易读,不需要任何包或选项:

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

perl -e 'map {$x += $_} <>; print $x' infile.txt - Avi Tevet
需要的内存量几乎为2GB,用于10百万个数字的大型输入。 - Amit Naidu

6

这里有一个漂亮干净的一行代码,使用的是Raku(之前称为Perl 6):

say [+] slurp.lines

我们可以这样使用它:
% seq 10 | raku -e "say [+] slurp.lines"
55

它的工作原理如下:
没有任何参数的slurp默认从标准输入读取;它返回一个字符串。在字符串上调用lines方法,将返回该字符串的逐行列表。
代码中括号中的+将其转换为约简元操作符,这个元操作符将列表“约简”为单个值:列表中各值的总和。say函数然后将其带有换行符的输出打印到标准输出流。
需要注意的一件事是,我们从未将行显式转换为数字 - Raku聪明到足以替我们完成此操作。但是,这意味着我们的代码在明确不是数字的输入上会出现问题。
% echo "1\n2\nnot a number" | raku -e "say [+] slurp.lines"
Cannot convert string to number: base-10 number must begin with valid digits or '.' in '⏏not a number' (indicated by ⏏)
  in block <unit> at -e line 1

“say [+] lines”其实已经足够了 :-) - Elizabeth Mattijsen
@ElizabethMattijsen:太酷了!这是怎么实现的? - Julia
lines没有任何参数时与slurp的语义相同,但它生成一个Str序列,而不是单个Str - Elizabeth Mattijsen

4
以下内容应该可以生效(假设您的数字是每行第二个字段)。
awk 'BEGIN {sum=0} \
 {sum=sum + $2} \
END {print "tot:", sum}' Yourinputfile.txt

2
你其实不需要 {sum=0} 这部分。 - Uphill_ What '1

4

如果你感觉熟悉的话,你可以用Python来完成:

以下内容未经测试,仅供参考:

out = open("filename").read();
lines = out.split('\n')
ints = map(int, lines)
s = sum(ints)
print s

Sebastian指出了一行脚本:

cat filename | python -c"from fileinput import input; print sum(map(int, input()))"

python -c"from fileinput import input; print sum(map(int, input()))" numbers.txt - jfs
3
猫命令被过度使用,需要从文件重定向标准输入: 在执行以下 Python 命令时,将标准输入重定向到 numbers.txt 文件: python -c "..." < numbers.txt - Giacomo
2
@rjack:cat的作用是展示脚本既适用于标准输入,也适用于在argv[]中的文件(就像Perl中的 while(<>) )。如果你的输入是来自于一个文件,那么 '<' 就不必要了。 - jfs
2
但是 <numbers.txt 演示了它可以像 cat numbers.txt | 一样在标准输入上正常工作。而且它不会教授不良习惯。 - Xiong Chiamiov
如果你很在意习惯,那么使用符号 command < file 本身就是一个不好的习惯。应该使用 < file command。Bash 的单行命令应该易于从输入到输出从左到右阅读。 - Marcel Besixdouze

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)

我必须点赞这条评论。回答没有任何问题 - 它非常好。然而,为了展示评论让答案变得更棒,我只是点赞了评论。 - bballdave025
厉害了,这个版本的性能和gcc编译的C语言版本一样出色。 - undefined
吹毛求疵:int 应该改为 long。在 EOF) 之间应该有一个换行符。 - undefined

3

Racket中的一行代码:

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

这个很慢,比C版本慢25倍。 - undefined

3
$ 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的回答,他提供了一种避免读入整个文件的解决方案。


3

My version:

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

2
简短版:seq -s+ -5 10 | bc - agc

2

提前道歉,由于反引号(“`”)的可读性较差,但在除bash外的其他shell中起作用,因此更易于复制粘贴。如果您使用接受它的shell,则$(command ...)格式比 `command ...` 更易读(也更易于调试),因此请随意修改以保持理智。

我在我的bashrc中有一个简单的函数,将使用awk计算多个简单的数学问题。

calc(){
  awk 'BEGIN{print '"$@"' }'
}

这将包括使用加、减、乘、除、幂、余数、平方根、正弦、余弦和括号等操作符(具体取决于您所使用的 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
}

6
你使用的是哪种不支持 $() 的古老 shell?我需要翻译这句话。 - alexia

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接