Arithmetic Expressions in BASH

10:21:00 AM 0 Comments

Arithmetic Expressions in BASH


First of all in BASH you can (and should) use $(( )) or let for integer arithmetic expressions. For example:

	i=$(( i + 1 ))
let i+=1

or

	i=$(( i++ ))
let i++

You must not have spaces around the equals sign, as
with any bash variable assignment.
As
long as you keep to integer arithmetic, you can use all the standard C-language
operators inside of $(()). Spaces are treated as in any normal algorithmic
language: they are options.  But again, please be aware that space before equal
sign confuses bash into thinking that this is a function invocation:

	i = $(( i + 5 )) # not what you think!

In this case bash will try to run a program
named i and its first argument would be an equal
sign, and its second argument would be the number you get adding 5 to
the value of $i.


Another oddity to these expressions is that the R-value rule ($ should be
prefixed to the shell variable on the right side of assignment statement to get
its value) is not applicable inside the double parentheses. But at the same time
you do need the dollar sign for positional parameters (e.g.,$2) to
distinguish it from a numeric constant (e.g., "2"). Here's an example:

	i=$(( i + $2 ))

There is a several shortcuts supported by BASH. One is += type of operators (familiar to any programmer):

	let i=i+5

can be written as

	let i+=5
Usual C-style increments work too:
	let i++
or
	(( i++ ))

Explanation of assignment operators in
bash





































































Operator Operation with
assignment
Use Meaning
=
Simple assignment a=b a=b
*=
Multiplication a*=b a=(a*b)
/=
Division a/=b a=(a/b)
%=
Remainder a%=b a=(a%b)
+=
Addition a+=b a=(a+b)
-=
Subtraction a-=b a=(a-b)
<<= Bit-shift left a<<=b a=(a<<b)
>>= Bit-shift right a>>=b a=(a>>b)
&= Bitwise "and" a&=b a=(a&b)
^=
Bitwise "exclusive or" a^=b a=(a^b)
|=
Bitwise "or" a|=b a=(a|b)
 

These assignment operators are also available with $(( )) provided they occur inside the double parentheses.
The outermost assignment is still just plain old shell variable assignment.


The assignments can also be cascaded, through the use of the comma
operator:

	echo $(( X+=2 , Y++ ))

which will do both assignments and then echo the result of the second
expression (since the comma operator returns the value of its second
expression).


Unlike many other places in bash scripts where
certain characters have special meanings (like the asterisk for wildcard
patterns or parentheses for subshell execution), in these expressions we don't
need to use quotes or backslashes to escape them since they don't have their
special meaning in let statements or inside of the $(( ))
construct:

	let Y=(X+2)*10

Y=$(( ( X + 2 ) * 10 ))

The $(( )) syntax allows all sorts of whitespace within the parentheses. For
that reason, it is both less prone to errors and makes the code much more
readable and is, therefore, our preferred way of doing bash integer arithmetic. However, an exception can be
made for the occasional += assignment or ++ operator, or when
we get nostalgic for the early days of BASIC programming (which had a
LET statement).


The differences between the ((...)) command and let
command


There are two major differences:


  • all characters between the (( and )) are treated as
    quoted (no macro expension).

  • The let statement requires that there be no spaces around not only
    the assignment operator (the equal sign), but around any of the other operators
    as well; it must all be packed together into a single word. You need to use
    quotes if you want to use spaces between tokens of the expression for
    example
    let "i = i +  1"

The ((...)) command is more convenient to use than let, because some of the
arithmetic operators (like *) have special meaning to the shell.  The only
contract that is often used with let is inrement like in



let i++


This commands is equivalent to:

let i=i+1 

and to

(( i=i + 1 ))
as well as to
(( i++ )) 
Before the Korn shell introduced let and (( ... )) commands, the only way to perform arithmetic was
with expr. For example, to do the same
increment X operation using expr:
X=`expr $X + 1` 

There is a similar mechanism for integer arithmetic with shell variables
using the bash built-in let statement.
It uses the same arithmetic operators as the $(( )) construct:

	let i=i+5 

When using let, there are some fancy assignment operators we can use
such as this (which will accomplish the same thing as the previous line):

	let i+=5
The ((...)) command also can be used in any control statements.
for example:
function mess
{
if (( "$1" > 0 )) ; then>
total=$1
else
total=100
fi
tail -$total /var/adm/messages | more

}

Using Array Variables


Initialization of arrays in bash has format similar to Perl:

	solaris=(serv01 serv02 serv07 ns1 ns2)

Each element of the array is a separate word in the list enclosed in
parentheses. Then you can refer to each this way:

	echo solaris is installed on ${solaris[2]}

If you omit index writing  echo
$solaris
you will get the first element too.

Another example taken
from Bash Shell
Programming in Linux
array=(red green blue yellow magenta)
len=${#array[*]}
echo "The array has $len members. They are:"
i=0
while [ $i -lt $len ]; do
echo "$i: ${array[$i]}"
let i++
done
Run this example:

 

$ ./myscript.sh
 

The array has 5 members. They are:
0: red
1: green
2: blue
3: yellow
4: magenta







If you're used to a "standard" *NIX shell you may not be familiar with bash's
array feature. Although not as powerful as similar constructs in the P languages
(Perl, Python, and PHP) and others, they are often quite useful.


Bash arrays have numbered indexes only, but they are sparse, ie you don't
have to define all the indexes. An entire array can be assigned by enclosing the
array items in parenthesis:

  arr=(Hello World)
Individual items can be assigned with the familiar array syntax (unless
you're used to Basic or Fortran):
  arr[0]=Hello
arr[1]=World
But it gets a bit ugly when you want to refer to an array item:
  echo ${arr[0]} ${arr[1]}
To quote from the man page:
The braces are required to avoid conflicts with pathname expansion.

 


In addition the following funky constructs are available:

  ${arr[*]}         # All of the items in the array
${!arr[*]} # All of the indexes in the array
${#arr[*]} # Number of items in the array
${#arr[0]} # Length if item zero
The ${!arr[*]} is a relatively new addition to bash, it was not
part of the original array implementation.

 


The following example shows some simple array usage (note the "[index]=value"
assignment to assign a specific index):


#!/bin/bash

array=(one two three four [5]=five)

echo "Array size: ${#array[*]}"

echo "Array items:"
for item in ${array[*]}
do
printf " %s\n" $item
done

echo "Array indexes:"
for index in ${!array[*]}
do
printf " %d\n" $index
done

echo "Array items and indexes:"
for index in ${!array[*]}
do
printf "%4d: %s\n" $index ${array[$index]}
done
Running it produces the following output:
Array size: 5
Array items:
one
two
three
four
five
Array indexes:
0
1
2
3
5
Array items and indexes:
0: one
1: two
2: three
3: four
5: five

Note that the "@" sign can be used instead of the "*" in constructs such as
${arr[*]}, the result is the same except when expanding to the items of
the array within a quoted string. In this case the behavior is the same as when
expanding "$*" and "$@" within quoted strings: "${arr[*]}" returns all
the items as a single word, whereas "${arr[@]}" returns each
item as a separate word.


The following example shows how unquoted, quoted "*", and quoted "@" affect
the expansion (particularly important when the array items themselves contain
spaces):


#!/bin/bash

array=("first item" "second item" "third" "item")

echo "Number of items in original array: ${#array[*]}"
for ix in ${!array[*]}
do
printf " %s\n" "${array[$ix]}"
done
echo

arr=(${array[*]})
echo "After unquoted expansion: ${#arr[*]}"
for ix in ${!arr[*]}
do
printf " %s\n" "${arr[$ix]}"
done
echo

arr=("${array[*]}")
echo "After * quoted expansion: ${#arr[*]}"
for ix in ${!arr[*]}
do
printf " %s\n" "${arr[$ix]}"
done
echo

arr=("${array[@]}")
echo "After @ quoted expansion: ${#arr[*]}"
for ix in ${!arr[*]}
do
printf " %s\n" "${arr[$ix]}"
done
When run it outputs:
Number of items in original array: 4
first item
second item
third
item

After unquoted expansion: 6
first
item
second
item
third
item

After * quoted expansion: 1
first item second item third item

After @ quoted expansion: 4
first item
second item
third
item

Mitch Frazier is the System Administrator at Linux
Journal
.


Arithmetic expansion with double
parentheses
, and using let 


The use of backticks (backquotes) in arithmetic expansion has been superseded by
double parentheses -- ((...)) and $((...))
-- and also by the very convenient let
construction.
z=$(($z+3))
z=$((z+3))                                 # Also correct.
                                           # Within double parentheses,
                                           #+ parameter dereferencing
                                           #+ is optional.

# $((EXPRESSION)) is arithmetic expansion. # Not to be confused with
                                           #+ command substitution.



# You may also use operations within double parentheses without assignment.

n=0
echo "n = $n"                            # n = 0

(( n += 1 ))                             # Increment.
# (( $n += 1 )) is incorrect!
echo "n = $n"                            # n = 1


let z=z+3
let "z += 3" # Quotes permit the use of spaces in variable assignment.
             # The 'let' operator actually performs arithmetic evaluation,
             #+ rather than expansion.







Some say he’s half man half fish, others say he’s more of a seventy/thirty split. Either way he’s a fishy bastard.