Bash Shell Scripting/Shell Arithmetic

Arithmetic expressions in Bash are closely modeled on those in C, so they are very similar to those in other C-derived languages, such as C++, Java, Perl, JavaScript, C#, and PHP. One major difference is that Bash only supports integer arithmetic (whole numbers), not floating-point arithmetic (decimals and fractions); something like  means what you'd expect (7), but something like   is a syntax error. Something like  is fine, but performs integer division, so evaluates to 2 rather than to 2.6.

Arithmetic expansion
Perhaps the most common way to use arithmetic expressions is in arithmetic expansion, where the result of an arithmetic expression is used as an argument to a command. Arithmetic expansion is denoted. For example, this command:

prints.

expr (deprecated)
Another way to use arithmetic expressions is using the Unix program "expr", which was popular before Bash supported math. Similar to Arithmetic expansion, this command:

prints. Note that using "expr" requires an escape character "\" before the multiplication operator "*" and parentheses. Further note the spaces between each operator symbol, including the parentheses.

Numeric operators
In addition to the familiar notations  (addition) and   (subtraction), arithmetic expressions also support   (multiplication),   (integer division, described above),   (modulo division, the "remainder" operation; for example, 11 divided by 5 is 2 remainder 1, so   is  ), and   ("exponentiation", i.e. involution; for example, 24 = 16, so   is  ).

The operators  and , in addition to their "binary" (two-operand) senses of "addition" and "subtraction", have "unary" (one-operand) senses of "positive" and "negative". Unary  has basically no effect; unary   inverts the sign of its operand. For example,  evaluates to , and   evaluates to.

Referring to variables
Inside an arithmetic expression, shell variables can be referred to directly, without using variable expansion (that is, without the dollar sign ). For example, this:

prints. (Note that  is evaluated first, producing 5, and then it's multiplied by 7. If we had written   rather than , mere string substitution would have been performed;   equals 14 + 3, that is, 17 — probably not what we want.)

The previous example shown using "expr":

prints.

Assigning to variables
Shell variables can also be assigned to within an arithmetic expression. The notation for this is similar to that of regular variable assignment, but is much more flexible. For example, the previous example could be rewritten like this:

except that this sets  to   rather than to. Note that, although arithmetic expansion looks a bit like command substitution, it is not executed in a subshell; this command actually sets  to , and later commands can use the new value. (The parentheses inside the arithmetic expression are just the normal mathematical use of parentheses to control the order of operations.)

In addition to the simple assignment operator, Bash also supports compound operators such as  ,  ,  ,  , and  , which perform an operation followed by an assignment. For example,  is equivalent to. In each case, the expression as a whole evaluates to the new value of the variable; for example, if  is , then   sets both   and   to.

Lastly, Bash supports increment and decrement operators. The increment operator  increases a variable's value by 1; if it precedes the variable-name (as the "pre-increment" operator), then the expression evaluates to the variable's new value, and if it follows the variable-name (as the "post-increment" operator), then the expression evaluates to the variable's old value. For example, if  is , then   sets both   and   to  , while   sets   to   and   to. The decrement operator  is exactly the same, except that it decreases the variable's value by 1. Pre-decrement and post-decrement are completely analogous to pre-increment and post-increment.

Arithmetic expressions as their own commands
A command can consist entirely of an arithmetic expression, using either of the following syntaxes:

Either of these commands will set  to. Both styles of command return an exit status of zero ("successful" or "true") if the expression evaluates to a non-zero value, and an exit status of one ("failure" or "false") if the expression evaluates to zero. For example, this:

will print this:

zero non-zero

The reason for this counterintuitive behavior is that in C, zero means "false" and non-zero values (especially one) mean "true". Bash maintains that legacy inside arithmetic expressions, then translates it into the usual Bash convention at the end.

The comma operator
Arithmetic expressions can contain multiple sub-expressions separated by commas. The result of the last sub-expression becomes the overall value of the full expression. For example, this:

sets  to , sets   to  , and prints.

The  built-in actually supports multiple expressions directly without needing a comma; therefore, the following three commands are equivalent:

Comparison, Boolean, and conditional operators
Arithmetic expressions support the integer comparison operators,  ,   (meaning ≤),   (meaning ≥),   (meaning =), and   (meaning ≠). Each evaluates to  for "true" or   for "false".

They also support the Boolean operators  ("and"), which evaluates to   if either of its operands is zero, and to   otherwise;   ("or"), which evaluates to   if either of its operands is nonzero, and to   otherwise; and   ("not"), which evaluates to   if its operand is zero, and to   otherwise. Aside from their use of zero to mean "false" and nonzero values to mean "true", these are just like the operators,  , and   that we've seen outside arithmetic expressions. Like those operators, these are "short-cutting" operators that do not evaluate their second argument if their first argument is enough to determine a result. For example,  will not evaluate the   part, and therefore will not set   to , because the left operand of   is zero ("false").

And they support the conditional operator. This operator evaluates, and returns its result, if   is nonzero; otherwise, it evaluates   and returns its result.

These operators can be combined in complex ways:

Arithmetic for-loops
Above, we saw one style of for-loop, that looked like this:

Bash also supports another style, modeled on the for-loops of C and related languages, using shell arithmetic:

This for-loop uses three separate arithmetic expressions, separated by semicolons  (and not commas   — these are completely separate expressions, not just sub-expressions). The first is an initialization expression, run before the loop begins. The second is a test expression; it is evaluated before every potential loop iteration (including the first), and if it evaluates to zero ("false"), then the loop exits. The third is a counting expression; it is evaluated at the end of each loop iteration. In other words, this for-loop is exactly equivalent to this while-loop:

but, once you get used to the syntax, it makes it more clear what is going on.

Bitwise operators
In addition to regular arithmetic and Boolean operators, Bash also offers "bitwise" operators, meaning operators that operate on integers qua bit-strings rather than qua integers. If you are not already familiar with this concept, you can safely ignore these.

Just as in C, the bitwise operators are  (bitwise "and"),   (bitwise "or"),   (bitwise "exclusive or"),   (bitwise "not"),   (bitwise left-shift), and   (bitwise right-shift), as well as   and   and   (which include assignment, just like  ).

Integer literals
An integer constant is expressed as an integer literal. We have already seen many of these;, for example, is an integer literal denoting the number 34. All of our examples have been decimal (base ten) integer literals, which is the default; but in fact, literals may be expressed in any base in the range 2–64, using the notation  (with the base itself being expressed in base-ten). For example, this:

echo $(( 12 ))       # use the default of base ten (decimal) echo $(( 10#12 ))    # explicitly specify base ten (decimal) echo $(( 2#1100 ))   # base two (binary) echo $(( 8#14 ))     # base eight (octal) echo $(( 16#C ))     # base sixteen (hexadecimal) echo $(( 8 + 2#100 )) # eight in base ten (decimal), plus four in base two (binary)

will print  six times. (Note that this notation only affects how an integer literal is interpreted. The result of the arithmetic expansion is still expressed in base ten, regardless.)

For bases 11 through 36, the English letters A through Z are used for digit-values 10 through 35. This is not case-sensitive. For bases 37 through 64, however, it is specifically the lowercase English letters that are used for digit-values 10 through 35, with the uppercase letters being used for digit-values 36 through 61, the at-sign  being used for digit-value 62, and the underscore   being used for digit-value 63. For example,  denotes 256259 ( 62 × 642 + 36 × 64 + 3 ).

There are also two special notations: prefixing a literal with  indicates base-eight (octal), and prefixing it with   or   indicates base-sixteen (hexadecimal). For example,  is equivalent to , and   is equivalent to.

Integer variables
A variable may be declared as an integer variable — that is, its "integer attribute" may be "set" — by using this syntax:

After running the above command, any subsequent assignments to  will automatically cause the right-hand side to be interpreted as an arithmetic expression. For example, this:

is more or less equivalent to this:

except that the first version's  will continue to affect later assignments as well.

In the first version, note the use of quotes around the right-hand side of the assignment. Had we written, it would have meant "run the command   with the argument  , passing in the environment variable   set to  , and redirecting standard output into the file  "; which is to say, setting a variable's integer attribute doesn't affect the overall parsing of assignment statements, but merely controls the interpretation of the value that is finally assigned to the variable.

We can "unset" a variable's integer attribute, turning off this behavior, by using the opposite command:

The  built-in command has a number of other uses as well: there are a few other attributes a variable can have, and   has a few other features besides turning attributes on and off. In addition, a few of its properties bear note:


 * As with  and , the argument can be a variable assignment; for example,   sets  's integer attribute and sets it to.
 * As with  and , multiple variables (and/or assignments) can be specified at once; for example,   sets both  's integer attribute and  's.
 * When used inside a function,  implicitly localizes the variable (unless the variable is already local), which also has the effect of locally unsetting it (unless the assignment syntax is used).

Non-integer arithmetic
As mentioned above, Bash shell arithmetic only supports integer arithmetic. However, external programs can often be used to obtain similar functionality for non-integer values. In particular, the common Unix utility  is often used for this. The following command:

prints. Needless to say, since  is not so tightly integrated with Bash as shell arithmetic is, it is not as convenient; for example, something like this:

would, to support non-integers, become something like this:

Part of this is because we can no longer use an arithmetic for-loop; part of it is because referring to variables and assigning to variables is trickier now (since  is not aware of the shell's variables, only its own, unrelated ones); and part of it is because   communicates with the shell only via input and output.