Bash math expression

397 Views Asked by At

I need help with this its been busting my mind.

I have a read with a variable with integers 10 20 -30.

All separated by white space. I try to change the minus to plus and save it onto another variable but it's not saving. If I can't change to plus I would like to remove it so then I can do:

var=$((${num// /+/}))

So it can add all integers.

This is what I have:

read num
echo $num
sum=$num | sed -e 's/-/+/g'
echo $sum
3

There are 3 best solutions below

3
On

Using standard POSIX variable expansion and arithmetic:

#!/usr/bin/env sh

# Computes the sum of all arguments
sum () {
  # Save the IFS value
  _OIFS=$IFS

  # Set the IFS to + sign
  IFS=+

  # Expand the arguments with the IFS + sign
  # inside an arithmetic expression to get
  # the sum of all arguments.
  echo "$(($*))"

  # Restore the original IFS
  IFS=$_OIFS
}

num='10 20 -30'

# shellcheck disable=SC2086 # Intended word splitting of string into arguments
sum $num

More featured version with a join function:

#!/usr/bin/env sh

# Join arguments with the provided delimiter character into a string
# $1: The delimiter character
# $@: The arguments to join
join () {
  # Save the IFS value
  _OIFS=$IFS

  # Set the IFS to the delimiter
  IFS=$1

  # Shift out the delimiter from the arguments
  shift

  # Output the joined string
  echo "$*"

  # Restore the original IFS
  IFS=$_OIFS
}

# Computes the sum of all arguments
sum () {
  # Join the arguments with a + sign to form a sum expression
  sum_expr=$(join + "$@")

  # Submit the sum expression to a shell's arithmetic expression
  # shellcheck disable=SC2004 # $sum_expr needs its $ to correctly split terms
  echo "$(($sum_expr))"
}

num='10 20 -30'

# shellcheck disable=SC2086 # Intended word splitting of string into arguments
sum $num
3
On

Simply: whipe last slash:

num="10 20 -30"
echo $((${num// /+}))
0

Some details

*Bash battern substitution has nothing common with so called regular expression. Correct syntax is:

   ${parameter/pattern/string}

... If pattern begins with /, all matches of pattern are replaced with string. Normally only the first match is replaced. ...

See: man -Pless\ +/parameter.pattern.string bash

If you try your syntax:

echo ${num// /+/}
10+/20+/-30

Then

echo ${num// /+}
10+20+-30

Or even, to make this pretty:

echo ${num// / + }
10 + 20 + -30

But result will stay same:

echo $((  ${num// / + }  ))
0
1
On
sum=$num | sed -e 's/-/+/g'

With respect to what's present above, sum=$num and sed become two different commands. It's not grouped together as you wanted, which makes the sed ineffective.

Also, you'd need to echo $num

Solution is to group them together, like:

sum=`echo $num | sed -e 's/-/+/g`

OR

sum=$(echo $num | sed -e 's/-/+/g')

OR Rather, an alternate approach

sum=${num//-/+}