Shell command to sum integers, one per line?

Viewed 684625

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?

45 Answers

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

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

The one-liner version in Python:

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

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
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()
perl -lne '$x += $_; END { print $x; }' < infile.txt

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

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

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

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)

The following works in bash:

I=0

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

echo $I

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, perl, GNU's datamash and friends:

  • it uses standards utilities common in any unix environment
  • it does not depend on buffering and thus it does not choke with really long inputs.
  • it implies no particular precision limits -or integer size for that matter-, hello AWK friends!
  • no need for different code, if floating point numbers need to be added, instead.
  • it theoretically runs unhindered in the minimal of environments
sed 's/^/.+/' infile | bc | tail -1

Here's a nice and clean Raku (formerly known as Perl 6) one-liner:

say [+] slurp.lines

We can use it like so:

% seq 10 | raku -e "say [+] slurp.lines"
55

It works like this:

slurp without any arguments reads from standard input by default; it returns a string. Calling the lines method on a string returns a list of lines of the string.

The brackets around + turn + into a reduction meta operator which reduces the list to a single value: the sum of the values in the list. say then prints it to standard output with a newline.

One thing to note is that we never explicitly convert the lines to numbers—Raku is smart enough to do that for us. However, this means our code breaks on input that definitely isn't a number:

% 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

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

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

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
}

one simple solution would be to write a program to do it for you. This could probably be done pretty quickly in python, something like:

sum = 0
file = open("numbers.txt","R")
for line in file.readlines(): sum+=int(line)
file.close()
print sum

I haven't tested that code, but it looks right. Just change numbers.txt to the name of the file, save the code to a file called sum.py, and in the console type in "python sum.py"

UPDATED BENCHMARKS

So I synthetically generated 100 mn integers randomly distributed

between

0^0 - 1 

and

8^8 - 1

GENERATOR CODE

mawk2 '
BEGIN {
     __=_=((_+=_^=_<_)+(__=_*_*_))^(___=__)
     srand()
     ___^=___
     do  { 
           print int(rand()*___) 
  
     } while(--_)  }' | pvE9 > test_large_int_100mil_001.txt

     out9:  795MiB 0:00:11 [69.0MiB/s] [69.0MiB/s] [ <=> ]

  f='test_large_int_100mil_001.txt'
  wc5 < "${f}"

    rows = 100000000. | UTF8 chars = 833771780. | bytes = 833771780.

Odd / Even distribution of Last Digit

Odd  49,992,332
Even 50,007,668

AWK - Fastest, by a good margin (maybe C is faster I dunno)

in0:  795MiB 0:00:07 [ 103MiB/s] [ 103MiB/s] [============>] 100%            
( pvE 0.1 in0 < "${f}" | mawk2 '{ _+=$__ } END { print _ }'; )  

 7.64s user 0.35s system 103% cpu 7.727 total
     1  838885279378716

Perl - Quite Decent

 in0:  795MiB 0:00:10 [77.6MiB/s] [77.6MiB/s] [==============>] 100%            
( pvE 0.1 in0 < "${f}" | perl -lne '$x += $_; END { print $x; }'; )  
 
10.16s user 0.37s system 102% cpu 10.268 total

     1  838885279378716

Python3 - Slightly behind Perl

 in0:  795MiB 0:00:11 [71.5MiB/s] [71.5MiB/s] [===========>] 100%            
( pvE 0.1 in0 < "${f}" | python3 -c ; )  

 11.00s user 0.43s system 102% cpu 11.140 total
     1  838885279378716

RUBY - Decent

 in0:  795MiB 0:00:13 [61.0MiB/s] [61.0MiB/s] [===========>] 100%            
( pvE 0.1 in0 < "${f}" | ruby -e 'puts ARGF.map(&:to_i).inject(&:+)'; )  
15.30s user 0.70s system 101% cpu 15.757 total

     1  838885279378716

JQ - Slow

 in0:  795MiB 0:00:25 [31.1MiB/s] [31.1MiB/s] [========>] 100%            
( pvE 0.1 in0 < "${f}" | jq -s 'add'; )  

 36.95s user 1.09s system 100% cpu 37.840 total

     1  838885279378716

DC

- ( had to kill it after no response in minutes)

The beauty of awk is that, using 1 single stream of integers jotted, it can simultaneously be generating multiple concurrent (and possibly cross-interacting) sequences with barely any code at all :

jot - -10 399 | 

mawk2 '__+=($++NF+=__+=-($++NF+=(--$!_)*9^9-1)+($!_^=2))' CONVFMT='%.20g'

121     4261625501                  -4261625380
100     12397455993                 -3874204891
81      28281696469                 -3486784402
64      59662756915                 -3099363913
49      122037457303                -2711943424
36      246399437577                -2324522935
25      494735977625                -1937102446
16      991021637223                -1549681957
9       1983205535923               -1162261468
4       3967185912829               -774840979
1       7934759246149               -387420490
0       15869518492299              -1
1       31738649564111              387420488
4       63476524287249              774840977
9       126951886313041             1162261466
16      253902222944143             1549681955
25      507802508785867             1937102444
36      1015602693048837            2324522933
49      2031202674154301            2711943422
64      4062402248944755            3099363911
81      8124801011105191            3486784400

This is a less commonly known feature, but mawk-1 can directly generate formatted output without using printf() or sprintf() :

 jot - -11111111555359 900729999999999 49987777777556 | 
 
 mawk '$++NF=_+=$!__' CONVFMT='%+\047\043 30.f' OFS='\t' 

-11111111555359           -11,111,111,555,359.
38876666222197            +27,765,554,666,838.
88864443999753           +116,629,998,666,591.
138852221777309          +255,482,220,443,900.

188839999554865          +444,322,219,998,765.
238827777332421          +683,149,997,331,186.
288815555109977          +971,965,552,441,163.

338803332887533        +1,310,768,885,328,696.
388791110665089        +1,699,559,995,993,785.
438778888442645        +2,138,338,884,436,430.
488766666220201        +2,627,105,550,656,631.

538754443997757        +3,165,859,994,654,388.
588742221775313        +3,754,602,216,429,701.
638729999552869        +4,393,332,215,982,570.
688717777330425        +5,082,049,993,312,995.

738705555107981        +5,820,755,548,420,976.
788693332885537        +6,609,448,881,306,513.
838681110663093        +7,448,129,991,969,606.
888668888440649        +8,336,798,880,410,255.

With nawk, one even more obscure feature is being able to print out the exact IEEE 754 double precision floating point hex :

 jot - .00001591111137777 \
       9007299999.1111111111 123.990333333328 | 

nawk '$++NF=_+=_+= cos(exp(log($!__)/1.1))' CONVFMT='[ %20.13p ]' OFS='\t' \_=1 

0.00001591111137777     [   0x400fffffffbf27f8 ]
123.99034924443937200   [   0x401f1a2498670bcc ]
247.98068257776736800   [   0x40313bd908775e35 ]
371.97101591109537821   [   0x4040516a505a57a3 ]
495.96134924442338843   [   0x4050b807540a1c3a ]

619.95168257775139864   [   0x4060f800d1abb906 ]
743.94201591107935201   [   0x407112ffc8adec4a ]
867.93234924440730538   [   0x40810bab4a485ad9 ]
991.92268257773525875   [   0x4091089e1149c279 ]

1115.91301591106321212  [   0x40a10ac8cfb09c62 ]
1239.90334924439116548  [   0x40b10a7bfa7fa42d ]
1363.89368257771911885  [   0x40c109c2d1b9947c ]
1487.88401591104707222  [   0x40d10a2644d5ab3b ]

gawk w/ GMP is even more interesting - they're willing to provide comma-formatted hex on your behalf, plus strangely padding extra commas in the empty space to its left

=

jot -  .000591111137777 90079.1111111111 123.990333333328 | 

gawk -v PREC=20000 -nMbe '
              $++NF  = _ +=(15^16 * log($!__)/log(sqrt(10)))' \
              CONVFMT='< 0x %\04724.12x >' OFS=' | '   \_=1 

# rows skipped in the middle for illustration clarity
 
4339.662257777619743 | < 0x    ,   ,4e6,007,2f4,08a,b93,8b3 >
4463.652591110947469 | < 0x    ,   ,50f,967,27f,e5a,963,518 >
4835.623591110930647 | < 0x    ,   ,58d,250,b65,a8d,45d,b79 >
7315.430257777485167 | < 0x    ,   ,8eb,b36,ee9,fe6,149,da5 >
11779.082257777283303 | < 0x    ,   ,f4b,c34,a75,82a,826,abb >

12151.053257777266481 | < 0x    ,   ,fd7,3c2,25e,1ab,a09,bbf >
16738.695591110394162 | < 0x    ,  1,6b0,f3b,350,ed3,eca,c58 >
17978.598924443671422 | < 0x    ,  1,894,2f2,aba,a30,f63,bae >
20458.405591110225942 | < 0x    ,  1,c64,a40,87e,e35,4d4,896 >
23434.173591110091365 | < 0x    ,  2,108,186,96e,0dc,2ef,d46 >

31741.525924443049007 | < 0x    ,  2,e45,bae,b73,24f,981,637 >
32857.438924442998541 | < 0x    ,  3,014,3a7,b9e,daf,18c,c3e >
33849.361591109620349 | < 0x    ,  3,1b0,9b7,5f1,536,49c,74e >
41536.762257775939361 | < 0x    ,  3,e51,7c1,9b2,e74,516,220 >
45876.423924442409771 | < 0x    ,  4,58c,52d,078,edb,db4,4ba >

53067.863257775417878 | < 0x    ,  5,1aa,cf3,eed,33c,638,456 >
59391.370257775131904 | < 0x    ,  5,c73,38a,54d,b41,98d,a02 >
61127.234924441720068 | < 0x    ,  5,f6d,ce2,c40,117,6d2,6e7 >
66830.790257774875499 | < 0x    ,  6,944,fe1,378,9ea,235,7b0 >
71170.451924441600568 | < 0x    ,  7,0ce,de6,797,df3,009,35d >

76254.055591108335648 | < 0x    ,  7,9b0,f6d,03d,878,edf,97d >
83073.523924441760755 | < 0x    ,  8,5b0,aa9,7f7,a31,89a,f2e >
86669.243591108475812 | < 0x    ,  8,c0d,678,fa3,3b1,aad,f26 >
89149.050257775175851 | < 0x    ,  9,074,278,19d,4c7,443,a00 >
89769.001924441850861 | < 0x    ,  9,18e,464,ff9,0eb,ee4,4e1 >

but be weary of syntactical errors -

  • this is a selection of what's being printed out to STDOUT,
  • all 256 byte choices have been observed of what's being printed out, even if it's terminal window

=

   jot 3000 | 
   gawk -Me ' _=$++NF=____+=$++NF=___-= $++NF=__+=$++NF=\
             _^= exp(cos($++NF=______+=($1) %10 + 1))'   \
                                  ____=-111111089 OFMT='%32c`' 


 char >>[  --[ U+ 2 | 2 (ASCII) freq >>[ 8 sumtotal >>[ 45151 
 char >>[  --[ U+ 4 | 4 (ASCII) freq >>[ 11 sumtotal >>[ 45166 
 char >>[  --[ U+ 14 | 20 (ASCII) freq >>[ 9 sumtotal >>[ 45301 
 char >>[ + --[ U+ 2B | 43 (ASCII) freq >>[ 9 sumtotal >>[ 60645 
 char >>[ --[ U+ 9 | 9 (ASCII) freq >>[ 12 sumtotal >>[ 45216 
 char >>[ 8 --[ U+ 38 | 56 (ASCII) freq >>[ 1682 sumtotal >>[ 82522 
 char >>[ Q --[ U+ 51 | 81 (ASCII) freq >>[ 6 sumtotal >>[ 85040 
 char >>[ Y --[ U+ 59 | 89 (ASCII) freq >>[ 8 sumtotal >>[ 85105 
 char >>[ g --[ U+ 67 | 103 (ASCII) freq >>[ 10 sumtotal >>[ 85212 
 char >>[ p --[ U+ 70 | 112 (ASCII) freq >>[ 7 sumtotal >>[ 85411 
 char >>[ v --[ U+ 76 | 118 (ASCII) freq >>[ 7 sumtotal >>[ 85462 
 char >>[ ? --[ \216 \x8E | 142 (8-bit byte) freq >>[ 15 sumtotal >>[ 85653 
 char >>[ ? --[ \222 \x92 | 146 (8-bit byte) freq >>[ 13 sumtotal >>[ 85698 
 char >>[ ? --[ \250 \xA8 | 168 (8-bit byte) freq >>[ 9 sumtotal >>[ 85967 
 char >>[ ? --[ \307 \xC7 | 199 (8-bit byte) freq >>[ 7 sumtotal >>[ 86345 
 char >>[ ? --[ \332 \xDA | 218 (8-bit byte) freq >>[ 69 sumtotal >>[ 86576 
 char >>[ ? --[ \352 \xEA | 234 (8-bit byte) freq >>[ 6 sumtotal >>[ 86702 
 char >>[ ? --[ \354 \xEC | 236 (8-bit byte) freq >>[ 5 sumtotal >>[ 86713 
 char >>[ ? --[ \372 \xFA | 250 (8-bit byte) freq >>[ 11 sumtotal >>[ 86823 
 char >>[ ? --[ \376 \xFE | 254 (8-bit byte) freq >>[ 9 sumtotal >>[ 86859

Ok, here is how to do it in PowerShell (PowerShell core, should work on Windows, Linux and Mac)

Get-Content aaa.dat | Measure-Object -Sum
Related