950
votes

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. Through grepping for the relevant lines and a bit of sed reformatting I can list all of the timings in that file. I would like to work out the total. I can pipe this intermediate output to any command in order to do the final sum. I have always used expr in the past, but unless it runs in RPN mode I do not think it is going to cope with this (and even then it would be tricky).

How can I get the summation of integers?

30
This is very similar to a question I asked a while ago: stackoverflow.com/questions/295781/…An̲̳̳drew
I really like this question for the fact that there are a lot of possible correct (or at least working) answers.Francisco Canedo
This question feels like a problem for code golf. codegolf.stackexchange.com :)Gordon Bean

30 Answers

1454
votes

Bit of awk should do it?

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

Note: some versions of awk have some odd behaviours if you are going to be adding anything exceeding 2^31 (2147483647). See comments for more background. One suggestion is to use printf rather than print:

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

Paste typically merges lines of multiple files, but it can also be used to convert individual lines of a file into a single line. The delimiter flag allows you to pass a x+x type equation to bc.

paste -s -d+ infile | bc

Alternatively, when piping from stdin,

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

The one-liner version in Python:

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

I would put a big WARNING on the commonly approved solution:

awk '{s+=$1} END {print s}' mydatafile # DO NOT USE THIS!!

that is because in this form awk uses a 32 bit signed integer representation: it will overflow for sums that exceed 2147483647 (i.e., 2^31).

A more general answer (for summing integers) would be:

awk '{s+=$1} END {printf "%.0f\n", s}' mydatafile # USE THIS INSTEAD
84
votes

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
69
votes

With jq:

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

Pure and short bash.

f=$(cat numbers.txt)
echo $(( ${f//$'\n'/+} ))
37
votes
perl -lne '$x += $_; END { print $x; }' < infile.txt
29
votes

My fifteen cents:

$ cat file.txt | xargs  | sed -e 's/\ /+/g' | bc

Example:

$ 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
26
votes

I've done a quick benchmark on the existing answers which

  • use only standard tools (sorry for stuff like lua or rocket),
  • are real one-liners,
  • are capable of adding huge amounts of numbers (100 million), and
  • are fast (I ignored the ones which took longer than a minute).

I always added the numbers of 1 to 100 million which was doable on my machine in less than a minute for several solutions.

Here are the results:

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

Paste & Bc

This ran out of memory on my machine. It worked for half the size of the input (50 million numbers):

:; seq 50000000 | paste -s -d+ - | bc
1250000025000000
# 17s
:; seq 50000001 100000000 | paste -s -d+ - | bc
3750000025000000
# 18s

So I guess it would have taken ~35s for the 100 million numbers.

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

Just for comparison's sake I compiled the C version and tested this also, just to have an idea how much slower the tool-based solutions are.

#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

Conclusion

C is of course fastest with 8s, but the Pypy solution only adds a very little overhead of about 30% to 11s. But, to be fair, Pypy isn't exactly standard. Most people only have CPython installed which is significantly slower (22s), exactly as fast as the popular Awk solution.

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

19
votes

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
17
votes

Plain bash one liner

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

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

You can using num-utils, although it may be overkill for what you need. This is a set of programs for manipulating numbers in the shell, and can do several nifty things, including of course, adding them up. It's a bit out of date, but they still work and can be useful if you need to do something more.

https://suso.suso.org/programs/num-utils/index.phtml

It's really simple to use:

$ seq 10 | numsum
55

But runs out of memory for large inputs.

$ seq 100000000 | numsum
Terminado (killed)
11
votes

The following works in bash:

I=0

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

echo $I
10
votes

Using the GNU datamash util:

seq 10 | datamash sum 1

Output:

55

If the input data is irregular, with spaces and tabs at odd places, this may confuse datamash, then either use the -W switch:

<commands...> | datamash -W sum 1

...or use tr to clean up the whitespace:

<commands...> | tr -d '[[:blank:]]' | datamash sum 1

If the input is large enough, the output will be in scientific notation.

seq 100000000 | datamash sum 1

Output:

5.00000005e+15

To convert that to decimal, use the the --format option:

seq 100000000 | datamash  --format '%.0f' sum 1

Output:

5000000050000000
9
votes

I realize this is an old question, but I like this solution enough to share it.

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

If there is interest, I'll explain how it works.

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

Cannot avoid submitting this, it is the most generic approach to this Question, please check:

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

It is to be found over here, I was the OP and the answer came from the audience:

And here are its special advantages over awk, bc and friends:

  • it does not depend on buffering and thus it does not choke with really long inputs.
  • it implies no particular precision -or integer size for that matter- limits - hello AWK users!
  • no need for different code, if floating point numbers need to be added, instead.
8
votes

Pure bash and in a one-liner :-)

$ 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
6
votes

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

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

For Ruby Lovers

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

The following should work (assuming your number is the second field on each line).

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

You can do it in python, if you feel comfortable:

Not tested, just typed:

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

Sebastian pointed out a one liner script:

cat filename | python -c"from fileinput import input; print sum(map(int, input()))"
3
votes
$ cat n
2
4
2
7
8
9
$ perl -MList::Util -le 'print List::Util::sum(<>)' < n
32

Or, you can type in the numbers on the command line:

$ perl -MList::Util -le 'print List::Util::sum(<>)'
1
3
5
^D
9

However, this one slurps the file so it is not a good idea to use on large files. See j_random_hacker's answer which avoids slurping.

3
votes

One-liner in Racket:

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

C (not simplified)

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)
3
votes

My version:

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

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

2
votes

Apologies in advance for readability of the backticks ("`"), but these work in shells other than bash and are thus more pasteable. If you use a shell which accepts it, the $(command ...) format is much more readable (and thus debuggable) than `command ...` so feel free to modify for your sanity.

I have a simple function in my bashrc that will use awk to calculate a number of simple math items

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

This will do +,-,*,/,^,%,sqrt,sin,cos, parenthesis ....(and more depending on your version of awk) ... you could even get fancy with printf and format floating point output, but this is all I normally need

for this particular question, I would simply do this for each line:

calc `echo "$@"|tr " " "+"`

so the code block to sum each line would look something like this:

while read LINE || [ "$LINE" ]; do
  calc `echo "$LINE"|tr " " "+"` #you may want to filter out some lines with a case statement here
done

That's if you wanted to only sum them line by line. However for a total of every number in the datafile

VARS=`<datafile`
calc `echo ${VARS// /+}`

btw if I need to do something quick on the desktop, I use this:

xcalc() { 
  A=`calc "$@"`
  A=`Xdialog --stdout --inputbox "Simple calculator" 0 0 $A`
  [ $A ] && xcalc $A
}