shell条件 - shell自增




Shell命令来整数整数,每行一个? (20)

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

作为一个背景,我有一个日志文件,其中包括时间测量,所以通过对相关行进行grep,以及一些sed重新格式化,我可以列出该文件中的所有时间。 然而,我想要计算出总数,而我的想法却一片空白,我可以用这个中间输出来执行最后的总和。 过去我一直使用expr ,但除非它运行在RPN mode ,否则我不认为它会应付这种情况(即使这样也会很棘手)。

我错过了什么? 考虑到可能有几种方法可以实现这一目标,即使其他人已经发布了可以完成这项工作的不同解决方案,我也会很乐意阅读(并支持)任何可行的方法。

相关问题: 最短命令来计算在Unix上的一列输出的总和? (学分@Andrew

更新 :哇,正如所料,这里有一些很好的答案。 看起来,我一定会作为一般的command-line tool来深入检查awk


AWK已经被提及,所以另外我想建议你使用这种语言代替 GREP和SED来扫描原始日志文件。 一个合适的AWK脚本可以很容易地完成两者的工作,并且可以像Paul和Alf已经指出的那样计算出有趣的值。


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

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

然后用法:

addnums < /tmp/nums

Python中的单行版本:

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

awk位应该这样做?

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

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

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

以下应该工作(假设你的号码是每行的第二个字段)。

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

你可以使用num-utils,虽然它可能是你需要的矫枉过正。 这是一套用于在shell中操纵数字的程序,可以做几件漂亮的事情,当然包括添加它们。 这有点过时了,但是如果你需要做更多的事情,它们仍然可以工作,并且可以是有用的。

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


实时总结,让您监控某些数字处理任务的进度。

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


对于Ruby恋人

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

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

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

PS我希望评论第一个答案,但我没有足够的声望..


我已经对现有的答案做了一个快速的基准

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

Paste&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%到11%的很小开销 。 但是,公平地说,Pypy并不完全是标准的。 大多数人只安装了CPython,其速度明显较慢(22s),与流行的Awk解决方案一样快。

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


我的十五美分:

$ 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

我的版本:

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

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

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

球拍中的单线:

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

简单的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

简单的打击一个班轮

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

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

纯粹的打击和单线:-)

$ 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

纯粹的短打击。

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

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()

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




shell