Advanced Bash-Scripting Guide: An in-depth exploration of the art of shell scripting
Prev | Chapter 8. Operations and Related Topics | Next

# 8.4. Operator Precedence

In a script, operations execute in order of precedence: the higher precedence operations execute before the lower precedence ones. [1]

Table 8-1. Operator Precedence

Operator | Meaning | Comments
--- | --- | ---
`` |  | HIGHEST PRECEDENCE
`var++ var--` | post-increment, post-decrement | C-style operators
`++var --var` | pre-increment, pre-decrement |
`! ~` | negation | logical / bitwise, inverts sense of following operator
`**` | exponentiation | arithmetic operation
`* / %` | multiplication, division, modulo | arithmetic operation
`+ -` | addition, subtraction | arithmetic operation
`<< >>` | left, right shift | bitwise
`-z -n` | unary comparison | string is/is-not null
`-e -f -t -x, etc.` | unary comparison | file-test
`< -lt > -gt <= -le >= -ge` | compound comparison | string and integer
`-nt -ot -ef` | compound comparison | file-test
`== -eq != -ne` | equality / inequality | test operators, string and integer
`&` | AND | bitwise
`^` | XOR | exclusive OR, bitwise
`|` | OR | bitwise
`&& -a` | AND | logical, compound comparison
`|| -o` | OR | logical, compound comparison
`?:` | trinary operator | C-style
`=` | assignment | (do not confuse with equality test)
`*= /= %= += -= <<= >>= &=` | combination assignment | times-equal, divide-equal, mod-equal, etc.
`,` | comma | links a sequence of operations
`` |  | LOWEST PRECEDENCE

In practice, all you really need to remember is the following:

- The "My Dear Aunt Sally" mantra (multiply, divide, add, subtract) for the familiar arithmetic operations.

- The compound logical operators, &&, ||, -a, and -o have low precedence.

- The order of evaluation of equal-precedence operators is usually left-to-right.

Now, let's utilize our knowledge of operator precedence to analyze a couple of lines from the `/etc/init.d/functions file`, as found in the Fedora Core Linux distro.

       1 while [ -n "$remaining" -a "$retry" -gt 0 ]; do
       2
       3 # This looks rather daunting at first glance.
       4
       5
       6 # Separate the conditions:
       7 while [ -n "$remaining" -a "$retry" -gt 0 ]; do
       8 #       --condition 1-- ^^ --condition 2-
       9
      10 #  If variable "$remaining" is not zero length
      11 #+      AND (-a)
      12 #+ variable "$retry" is greater-than zero
      13 #+ then
      14 #+ the [ expresion-within-condition-brackets ] returns success (0)
      15 #+ and the while-loop executes an iteration.
      16 #  ==============================================================
      17 #  Evaluate "condition 1" and "condition 2" ***before***
      18 #+ ANDing them. Why? Because the AND (-a) has a lower precedence
      19 #+ than the -n and -gt operators,
      20 #+ and therefore gets evaluated *last*.
      21
      22 #################################################################
      23
      24 if [ -f /etc/sysconfig/i18n -a -z "${NOLOCALE:-}" ] ; then
      25
      26
      27 # Again, separate the conditions:
      28 if [ -f /etc/sysconfig/i18n -a -z "${NOLOCALE:-}" ] ; then
      29 #    --condition 1--------- ^^ --condition 2-----
      30
      31 #  If file "/etc/sysconfig/i18n" exists
      32 #+      AND (-a)
      33 #+ variable $NOLOCALE is zero length
      34 #+ then
      35 #+ the [ test-expresion-within-condition-brackets ] returns success (0)
      36 #+ and the commands following execute.
      37 #
      38 #  As before, the AND (-a) gets evaluated *last*
      39 #+ because it has the lowest precedence of the operators within
      40 #+ the test brackets.
      41 #  ==============================================================
      42 #  Note:
      43 #  ${NOLOCALE:-} is a parameter expansion that seems redundant.
      44 #  But, if $NOLOCALE has not been declared, it gets set to *null*,
      45 #+ in effect declaring it.
      46 #  This makes a difference in some contexts.

To avoid confusion or error in a complex sequence of test operators, break up the sequence into bracketed sections.

       1 if [ "$v1" -gt "$v2"  -o  "$v1" -lt "$v2"  -a  -e "$filename" ]
       2 # Unclear what's going on here...
       3
       4 if [[ "$v1" -gt "$v2" ]] || [[ "$v1" -lt "$v2" ]] && [[ -e "$filename" ]]
       5 # Much better -- the condition tests are grouped in logical sections.

### Notes

[1] | Precedence, in this context, has approximately the same meaning as priority

Prev | Home | Next
The Double-Parentheses Construct | Up | Beyond the Basics