19

In shell script we can substitute expr $a*$b with $(($a+$b)).

But why not just with (($a+$b)), because in any resource it is written that (()) is for integer computation.

So we use $(()) when there are variables instead of integer values do we? And what should we use instead of $(()) when variables can receive float values?

jasonwryan
  • 73,126
Stranger
  • 317

3 Answers3

32
  1. For arithmetic, expr is archaic. Don't use it.*

  2. $((...)) and ((...)) are very similar. Both do only integer calculations. The difference is that $((...)) returns the result of the calculation and ((...)) does not. Thus $((...)) is useful in echo statements:

     $ a=2; b=3; echo "$((a*b))"
     6
    

    ((...)) is useful when you want to assign a variable or set an exit code:

     $ a=3; b=3; ((a==b)) && echo yes
     yes
    
  3. If you want floating point calculations, use bc or awk:

    $ echo '4.7/3.14' | bc -l
    1.49681528662420382165
    

    $ awk 'BEGIN{print 4.7/3.14}' 1.49682

*As an aside, expr remains useful for string handling when globs are not good enough and a POSIX method is needed to handle regular expressions.

Kusalananda
  • 333,661
John1024
  • 74,655
2

expr is old, but it does have one limited use I can think of. Say you want to search a string. If you want to stay POSIX with grep, you need to use a pipe:

if echo november | grep nov 
then
  : do something
fi

expr can do this without a pipe:

if expr november : nov
then
  : do something
fi

the only catch is expr works with anchored strings, so if you want to match after the beginning you need to change the REGEXP:

if expr november : '.*ber'
then
  : do something
fi

Regarding (( )), this construct is not POSIX, so should be avoided.

Regarding $(( )), you do not have to include the dollar sign:

$ fo=1
$ go=2
$ echo "$((fo + go))"
3
Kusalananda
  • 333,661
Zombo
  • 1
  • 5
  • 44
  • 63
1

It seems that the following programs do more or less the same and they don't really differ. But that is not true.

#!/bin/bash 
s=-1000
for (( i=0; i<1000000; i++ )); do
    s=$((s+1))
done
echo "$s"

This is the correct way to implement this. The expression s+1 is evaluated by the shell and can be assigned to a variable.

#!/bin/bash
s=-1000
for (( i=0; i<1000000; i++ )); do
    s=`expr "$s" + 1`
done
echo "$s"

Here the expression will be calculated by the program expr, which isn't a shell builtin but an external Unix program. So instead of simply adding 1 and s a program must be started und its output must be read and written to the variable. Starting a programm needs a lot of resources and a lot of time. And this program is run 1000000 times. So the program will be much slower than the previous. Nevertheless the code works correctly.

#!/bin/bash -e
s=-1000
for (( i=0; i<1000000; i++ )); do
    ((s=s+1))
done
echo "$s"

If the -e flag isn't set, the program will work correctly, too. But if -e is set when s=-1 and ((s=s+1)) is calculated. The expression s=s+1 evaluates to 0 and the exit code of ((0)) is >0 which is interpreted as an error by the shell and the shell exits the program.

The reason to set the -e flag is that it is the simplest way of error processing: stop if an error ocurrs.

Kusalananda
  • 333,661
  • While the points about invoking external programs and the -e flag were correct, the code needed fixing. I edited the post: the for loops needed closing with a 'done', and the arguments of expr needed separating with spaces. – johnraff Mar 28 '23 at 02:52