Shortest command to calculate the sum of a column of output on Unix?
I know this question is somewhat dated, but I can't see "my" answer here, so I decided to post nonetheless. I'd go with a combination of
- tail (to get the lines you need)
- tr (to shrink down multiple consequitive spaces to one)
- cut (to get only the needed column)
- paste (to concatenate each line with a
+
sign) - bc (to do the actual calculation)
ipcs
doesn't give an output on my system, so I'll just demo it with df
:
# df
Filesystem 1K-blocks Used Available Use% Mounted on
rootfs 33027952 4037420 27312812 13% /
udev 10240 0 10240 0% /dev
tmpfs 102108 108 102000 1% /run
/dev/xvda1 33027952 4037420 27312812 13% /
tmpfs 5120 0 5120 0% /run/lock
tmpfs 204200 0 204200 0% /run/shm
/dev/xvda1 33027952 4037420 27312812 13% /var/www/clients/client1/web1/log
/dev/xvda1 33027952 4037420 27312812 13% /var/www/clients/client1/web2/log
/dev/xvda1 33027952 4037420 27312812 13% /var/www/clients/client1/web3/log
/dev/xvda1 33027952 4037420 27312812 13% /var/www/clients/client1/web4/log
/dev/xvda1 33027952 4037420 27312812 13% /var/www/clients/client2/web5/log
/dev/xvda1 33027952 4037420 27312812 13% /var/www/clients/client2/web6/log
# df | tail -n +2 | tr -s ' ' | cut -d ' ' -f 2 | paste -s -d+ | bc
264545284
I know doing this particular calculation on my system doesn't really make sense, but it shows the concept.
All of the pieces of this solution have been shown in the other answers, but never in that combination.
I have a utility script which simply adds up all columns. It's usually easy enough to grab the one you want from the one-line output. As a bonus, some SI-suffixes are recognized.
#!/usr/bin/awk -f
# Sum up numerical values by column (white-space separated)
#
# Usage: $0 [file ...]
#
# stern, 1999-2005
{
for(i = 1; i <= NF; ++i) {
scale = 1
if ($i ~ /[kK]$/) { scale = 1000 }
if ($i ~ /[mM]$/) { scale = 1000*1000 }
if ($i ~ /[gG]$/) { scale = 1000*1000*1000 }
col[i] += scale * $i;
}
if (NF > maxnf) maxnf = NF;
}
END {
for(i = 1; i <= maxnf; ++i) { printf " %.10g", col[i] }
print "";
}
Example with custom field separator:
$ head /etc/passwd | addcol -F:
0 0 45 39 0 0 0
ipcs -mb | tail +4 | awk '{ sum += $7 } END { print sum }'
Or without tail:
ipcs -mb | awk 'NR > 3 { sum += $7 } END { print sum }'
Using awk with bc to have arbitrary long results (credits to Jouni K.
):
ipcs -mb | awk 'NR > 3 { print $7 }' | paste -sd+ | bc
I would try to construct a calculation string and feed it to bc as follows:
- grep the lines that contain the numbers
- sed away all characters before (and after) the number on each line
- xargs the result (to get a string of numbers separated by blanks)
- tr anslate the blanks to '+' characters
- good appetite bc!
ipcs -mb | grep -w '^m ' | sed 's/^.*\s//' | xargs | tr ' ' + | bc
Looks like this is slightly longer than the awk solution, but for everyone who can't read (and understand) the odd awk code this may be easier to grasp... :-)
If bc is not installed you can use double parentheses in step 5 above to calculate the result:
echo $(( $(ipcs -mb | grep -w '^m ' | sed 's/^.*\s//' | xargs | tr ' ' +) ))
orSUM=$(( $(ipcs -mb | grep -w '^m ' | sed 's/^.*\s//' | xargs | tr ' ' +) ))
or(( SUM=$(ipcs -mb | grep -w '^m ' | sed 's/^.*\s//' | xargs | tr ' ' +) ))
The spacing after and before the double parentheses is optional.