Shell command to sum integers, one per line?

2019-01-01 11:53发布

I am looking for a command that will accept as input multiple lines of text, each line containing a single integer, and output the sum of these integers.

As a bit of background, I have a log file which includes timing measurements, so through grepping for the relevant lines, and a bit of sed reformatting I can list all of the timings in that file. I'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've always used expr in the past, but unless it runs in RPN mode I don't think it's going to cope with this (and even then it would be tricky).

What am I missing? Given that there are probably several ways to achieve this, I will be happy to read (and upvote) any approach that works, even if someone else has already posted a different solution that does the job.

Related question: Shortest command to calculate the sum of a column of output on Unix? (credits @Andrew)


Update: Wow, as expected there are some nice answers here. Looks like I will definitely have to give awk deeper inspection as a command-line tool in general!

标签: shell
30条回答
泪湿衣
2楼-- · 2019-01-01 11:58

Plain 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
查看更多
流年柔荑漫光年
3楼-- · 2019-01-01 11:59

The one-liner version in Python:

$ python -c "import sys; print(sum(int(l) for l in sys.stdin))"
查看更多
谁念西风独自凉
4楼-- · 2019-01-01 12:00
dc -f infile -e '[+z1<r]srz1<rp'

Note that negative numbers prefixed with minus sign should be translated for dc, since it uses _ prefix rather than - prefix for that. For example, via tr '-' '_' | dc -f- -e '...'.

Edit: Since this answer got so many votes "for obscurity", here is a detailed explanation:

The expression [+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

As pseudo-code:

  1. Define "add_top_of_stack" as:
    1. Remove the two top values off the stack and add the result back
    2. If the stack has two or more values, run "add_top_of_stack" recursively
  2. If the stack has two or more values, run "add_top_of_stack"
  3. Print the result, now the only item left in the stack

To really understand the simplicity and power of dc, here is a working Python script that implements some of the commands from dc and executes a Python version of the above command:

### 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()
查看更多
像晚风撩人
5楼-- · 2019-01-01 12:00

Alternative pure Perl, fairly readable, no packages or options required:

perl -e "map {$x += $_} <> and print $x" < infile.txt
查看更多
闭嘴吧你
6楼-- · 2019-01-01 12:00

C++ (simplified):

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

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

SCC is C++ snippets evaluator at shell prompt

查看更多
素衣白纱
7楼-- · 2019-01-01 12:01

BASH solution, if you want to make this a command (e.g. if you need to do this frequently):

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

Then usage:

addnums < /tmp/nums
查看更多
登录 后发表回答