Austen
Austen

Reputation: 293

Bash arithmetic issues

I have the following script, pretty self explanatory but I'm getting super strange issues.

Multiply gives me 0

Division multiplies

Any insight would be awesome!

Thanks, Austen

n1=$1
op=$2
n2=$3


case "$op" in

+)
        echo $(( $n1 + $n2 ))
        ;;
-)
        echo $(( $n1 - $n2 ))
        ;;

*)      echo $(( $n1 * $n2 ))
        ;;

/)      echo $(( $n1 / $n2 ))
        ;;
esac

Upvotes: 1

Views: 119

Answers (4)

mklement0
mklement0

Reputation: 437608

@kojiro's answer explains the problem with the original code well (* must be quoted to prevent its interpretation as the default case branch inside the script, and to prevent pathname expansion when passed as an argument).

@kojiro also points out that you can use variable references in place of literal arithmetic operators in arithmetic expansion expressions ($((...))). Caveat: only works as $op - with $ prefix! - not just op (the $ prefix is otherwise optional in arithmetic contexts in bash).

With that in mind, here's a simplified solution that also performs operator checking. (To make it fully robust, the operands would have to be checked, too).

#!/bin/bash

n1=$1 op=$2 n2=$3

 # Allow 'x' to be passed as an alias for '*' (multiplication).
[[ "$op" == 'x' ]] && op='*'

 # Ensure that the operator is valid.
case $op in
    +|-|'*'|/) # note the quoted '*', needed to prevent interpretation as wildcard
        ;;
    *) # proper use of unquoted *: the everything-else branch
        echo "Error: Unexpected operator: $op" >&2
        exit 1 
esac

 # Perform the calculation. Note how the operator is provided via variable $op.
 # Note how the reference to variable `op` _must_ be `$`-prefixed in this case,
 # unlike the other two references.
echo $(( n1 $op n2 ))

Upvotes: 1

Austen
Austen

Reputation: 293

Fixed it, but this is how I had to fix it:

#!/bin/bash

n1=$1
op=$2
n2=$3

if [ "$op" == 'x' ]
then
echo $(( $n1 * $n2 ))
else
echo $(( $n1 $op $n2 ))
fi

Upvotes: 0

kojiro
kojiro

Reputation: 77107

@gniourf_gniourf's comment is 90% of the answer and fully explains why "division multiplies". The other 10% is that you need to quote the * argument when you pass it to this code, or else it will glob expand and your third argument will be some filename, which, inside the arithmetic expression, will probably evaluate to 0. That explains why "Multiply gives me 0".

You could also solve this by setting the noglob shell option to prevent globs from expanding.

Compare:

echo *
set -f
echo *

Upvotes: 3

Tiago Lopo
Tiago Lopo

Reputation: 7959

Consider using bc as bash does not handle floats:

echo $((4/3))
1

Using bc:

echo "4/3" | bc -l
1.33333333333333333333

Upvotes: 1

Related Questions