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+=5Usual 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+=5The ((...)) 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.
from Bash Shell
Programming in Linux
array=(red green blue yellow magenta)Run this example:
len=${#array[*]}
echo "The array has $len members. They are:"
i=0
while [ $i -lt $len ]; do
echo "$i: ${array[$i]}"
let i++
done
$ ./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]=HelloBut it gets a bit ugly when you want to refer to an array item:
arr[1]=World
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 arrayThe ${!arr[*]} is a relatively new addition to bash, it was not
${!arr[*]} # All of the indexes in the array
${#arr[*]} # Number of items in the array
${#arr[0]} # Length if item zero
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
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
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.