19

I am trying to add the elements of an array that is defined by user input from the read -a command. How can I do that?

Benjamin W.
  • 46,058
  • 19
  • 106
  • 116
BobbyT28
  • 281
  • 2
  • 3
  • 9

9 Answers9

32
read -a array
tot=0
for i in ${array[@]}; do
  let tot+=$i
done
echo "Total: $tot"
perreal
  • 94,503
  • 21
  • 155
  • 181
23

Given an array (of integers), here's a funny way to add its elements (in bash):

sum=$(IFS=+; echo "$((${array[*]}))")
echo "Sum=$sum"

e.g.,

$ array=( 1337 -13 -666 -208 -408 )
$ sum=$(IFS=+; echo "$((${array[*]}))")
$ echo "$sum"
42

Pro: No loop, no subshell!

Con: Only works with integers

Edit (2012/12/26).

As this post got bumped up, I wanted to share with you another funny way, using dc, which is then not restricted to just integers:

$ dc <<< '[+]sa[z2!>az2!>b]sb1 2 3 4 5 6 6 5 4 3 2 1lbxp'
42

This wonderful line adds all the numbers. Neat, eh?

If your numbers are in an array array:

$ array=( 1 2 3 4 5 6 6 5 4 3 2 1 )
$ dc <<< '[+]sa[z2!>az2!>b]sb'"${array[*]}lbxp"
42

In fact there's a catch with negative numbers. The number '-42' should be given to dc as _42, so:

$ array=( -1.75 -2.75 -3.75 -4.75 -5.75 -6.75 -7.75 -8.75 )
$ dc <<< '[+]sa[z2!>az2!>b]sb'"${array[*]//-/_}lbxp"
-42.00

will do.

Pro: Works with floating points.

Con: Uses an external process (but there's no choice if you want to do non-integer arithmetic — but dc is probably the lightest for this task).

Community
  • 1
  • 1
gniourf_gniourf
  • 44,650
  • 9
  • 93
  • 104
  • 3
    That went way over my head :-| – BobbyT28 Nov 29 '12 at 22:20
  • does not work on natural numbers: $ echo "${arrr[*]}" 2 1 34 3 2 $ IFS=+ read <<< "${arrr[*]}" then no + is provided: $ echo $REPLY 2 1 34 3 2 – Chris Oct 03 '19 at 16:45
  • The (very smart) first solution does create a subshell because command substitution (`$(...)`) in Bash _always_ creates a subshell. See [mklement0's answer](https://stackoverflow.com/a/21338131/4154375) to [When does command substitution spawn more subshells than the same commands in isolation?](https://stackoverflow.com/q/21331042/4154375). – pjh Nov 08 '19 at 19:03
9

My code (which I actually utilize) is inspired by answer of gniourf_gniourf. I personally consider this more clear to read/comprehend, and to modify. Accepts also floating points, not just integers.

Sum values in array:

arr=( 1 2 3 4 5 6 7 8 9 10 )
IFS='+' sum=$(echo "scale=1;${arr[*]}"|bc)
echo $sum # 55

With small change, you can get the average of values:

arr=( 1 2 3 4 5 6 7 8 9 10 )
IFS='+' avg=$(echo "scale=1;(${arr[*]})/${#arr[@]}"|bc)
echo $avg # 5.5
Community
  • 1
  • 1
F-3000
  • 935
  • 13
  • 13
  • An ellegant, quick and even simple solution. Thanks you. – Sopalajo de Arrierez Jan 26 '16 at 00:10
  • 1
    FYI this can have consequences in other parts of a script when IFS is defined – Mike Q Jul 18 '19 at 16:15
  • 1
    Yes, @MikeQ. The IFS must be moved inside the subshell to prevent contaminating the running shell. `arr=( 1 2 3 4 5 6 7 8 9 10 ); echo $(IFS='+'; bc<<<"scale=1;(${arr[*]})/${#arr[@]}")` Also, you can use a "Here String" instead of echo to save 1 process. – Bruno Bronosky Feb 08 '20 at 02:02
  • 1
    @BrunoBronosky, then one could backup and restore IFS variable after the operation etc. .. – Mike Q Feb 08 '20 at 02:28
  • 1
    @MikeQ The way IFS is (re-)defined here, it will only be in effect for that single command line. Backing up and restoring IFS is not necessary here – SpinUp __ A Davis Jul 24 '22 at 04:47
7

gniourf_gniourf's answer is excellent since it doesn't require a loop or bc. For anyone interested in a real-world example, here's a function that totals all of the CPU cores reading from /proc/cpuinfo without messing with IFS:

# Insert each processor core count integer into array
cpuarray=($(grep cores /proc/cpuinfo | awk '{print $4}'))
# Read from the array and replace the delimiter with "+"
# also insert 0 on the end of the array so the syntax is correct and not ending on a "+"
read <<< "${cpuarray[@]/%/+}0"
# Add the integers together and assign output to $corecount variable
corecount="$((REPLY))"
# Echo total core count
echo "Total cores: $corecount"

I also found the arithmetic expansion works properly when calling the array from inside the double parentheses, removing the need for the read command:

cpuarray=($(grep cores /proc/cpuinfo | awk '{print $4}'))
corecount="$((${cpuarray[@]/%/+}0))"
echo "Total cores: $corecount"

Generic:

array=( 1 2 3 4 5 )
sum="$((${array[@]/%/+}0))"
echo "Total: $sum"
Banana
  • 159
  • 2
  • 5
6

I'm a fan of brevity, so this is what I tend to use:

IFS="+";bc<<<"${array[*]}"

It essentially just lists the data of the array and passes it into BC which evaluates it. The "IFS" is the internal field separate, it essentially specifies how to separate arrays, and we said to separate them with plus signs, that means when we pass it into BC, it receives a list of numbers separated by plus signs, so naturally it adds them together.

amihart
  • 171
  • 1
  • 6
3

Another dc & bash method:

arr=(1 3.88 7.1 -1)
dc -e "0 ${arr[*]/-/_} ${arr[*]/*/+} p"

Output:

10.98

The above runs the expression 0 1 3.88 7.1 _1 + + + + p with dc. Note the dummy value 0 because there's one too many +s, and also note the usual negative number prefix - must be changed to _ in dc.

agc
  • 7,973
  • 2
  • 29
  • 50
2
arr=(1 2 3) //or use `read` to fill the array
echo Sum of array elements: $(( ${arr[@]/%/ +} 0))
Sum of array elements: 6

Explanation:

  1. "${arr[@]/%/ +}" will return 1 + 2 + 3 +
  2. By adding additional zero at the end we will get 1 + 2 + 3 + 0
  3. By wrapping this string with BASH's math operation like this$(( "${arr[@]/%/ +} 0")), it will return the sum instead

This could be used for other math operations.

  1. For subtracting just use - instead
  2. For multiplication use * and 1 instead of 0

Can be used with logic operators too.

  1. BOOL AND EXAMPLE - check if all items are true (1)

    arr=(1 0 1)

    if [[ $((${arr[@]/%/ &} 1)) -eq 1 ]]; then echo "yes"; else echo "no"; fi

    This will print: no

  2. BOOL OR EXAMPLE - check if any item is true (1)

    arr=(1 0 0)

    if [[ $((${arr[@]/%/ |} 0)) -eq 1 ]]; then echo "yes"; else echo "no"; fi

    This will print: yes

Vladimir Djuricic
  • 4,323
  • 1
  • 21
  • 22
0

I find this very simple using an increasing variable:

result2=0
for i  in ${lineCoffset[@]};
do
    result2=$((result2+i))  
done
echo $result2
Benjamin W.
  • 46,058
  • 19
  • 106
  • 116
0

A simple way

function arraySum 
{
        sum=0
        for i in ${a[@]};
        do
              sum=`expr $sum + $i`  
        done
        echo $sum
}

a=(7 2 3 9)
echo -n "Sum is = " 
arraySum ${a[@]}
rashedcs
  • 3,588
  • 2
  • 39
  • 40