Skip to main content

2. More testing constructions

  1. The compound command [[ expression ]]

    Modern versions of bash include a compound command that acts as an enhanced replacement for test: [[ expression ]] . It has also an operator for regular expression matching: =~.

    test-integer2.sh
    #!/bin/bash

    # test-integer2: evaluate the value of an integer.

    INT=-5

    if [[ "$INT" =~ ^-?[0-9]+$ ]]; then
    if [ "$INT" -eq 0 ]; then
    echo "INT is zero."
    else
    if [ "$INT" -lt 0 ]; then
    echo "INT is negative."
    else
    echo "INT is positive."
    fi
    if [ $((INT % 2)) -eq 0 ]; then
    echo "INT is even."
    else
    echo "INT is odd."
    fi
    fi
    else
    echo "INT is not an integer." >&2
    exit 1
    fi
    vim test-integer2.sh
    ./test-integer2.sh
    More testing
    sed -i test-integer2.sh -e '/^INT=/c INT=11'
    ./test-integer2.sh

    sed -i test-integer2.sh -e '/^INT=/c INT='
    ./test-integer2.sh

    sed -i test-integer2.sh -e '/^INT=/c INT=0'
    ./test-integer2.sh

    sed -i test-integer2.sh -e '/^INT=/c INT=12'
    ./test-integer2.sh

    Another added feature of [[ ]] is that the == operator supports pattern matching the same way pathname expansion does:

    FILE=foo.bar
    if [[ $FILE == foo.* ]]; \
    then echo "$FILE matches pattern 'foo.*'"; fi
  2. The compound command (( integer expression ))

    In addition to the compound command [[ ]], bash also provides the compound command (( )), which is useful for operating on integers.

    if ((1)); then echo "It is true."; fi
    if ((0)); then echo "It is true."; fi
    if ((2)); then echo "It is true."; fi

    With this test command we can simplify a bit the previous example script:

    test-integer2a.sh
    #!/bin/bash

    # test-integer2a: evaluate the value of an integer.

    INT=-5

    if [[ "$INT" =~ ^-?[0-9]+$ ]]; then
    if ((INT == 0)); then
    echo "INT is zero."
    else
    if ((INT < 0)); then
    echo "INT is negative."
    else
    echo "INT is positive."
    fi
    if (( ((INT % 2)) == 0)); then
    echo "INT is even."
    else
    echo "INT is odd."
    fi
    fi
    else
    echo "INT is not an integer." >&2
    exit 1
    fi
    vim test-integer2a.sh
    ./test-integer2a.sh
    diff -u test-integer2.sh test-integer2a.sh \
    | highlight -S bash -O xterm256 2>/dev/null

    Notice that we don't use a $ sign to refer to variables inside (( )). Also, instead of -eq we use the operator ==, instead of -lt we use <, etc. This makes the syntax a bit more natural.

  3. We can use logical operators to create complex expressions. For the test (and [ ]) command the logical operators are -a (AND), -o (OR) and ! (NOT). For the commands [[ ]] and (( )) they are: &&, || and !.

    test-integer3.sh
    #!/bin/bash

    # test-integer3: determine if an integer is within a
    # specified range of values.

    MIN_VAL=1
    MAX_VAL=100
    INT=50

    if [[ ! "$INT" =~ ^-?[0-9]+$ ]]; then
    echo "INT is not an integer." >&2
    exit 1
    fi

    if [[ "$INT" -ge "$MIN_VAL" && "$INT" -le "$MAX_VAL" ]]; then
    echo "$INT is within $MIN_VAL to $MAX_VAL."
    else
    echo "$INT is out of range."
    fi

    echo -n "Using [[ ... ]]: "
    if [[ ! ("$INT" -ge "$MIN_VAL" && "$INT" -le "$MAX_VAL") ]]; then
    echo "$INT is outside $MIN_VAL to $MAX_VAL."
    else
    echo "$INT is in range."
    fi

    echo -n "Using (( ... )): "
    if (( ! (INT > MIN_VAL && INT < MAX_VAL) )); then
    echo "$INT is outside $MIN_VAL to $MAX_VAL."
    else
    echo "$INT is in range."
    fi

    echo -n "Using [ ... ] : "
    if [ ! \( "$INT" -ge "$MIN_VAL" -a "$INT" -le "$MAX_VAL" \) ];
    then
    echo "$INT is outside $MIN_VAL to $MAX_VAL."
    else
    echo "$INT is in range."
    fi
    vim test-integer3.sh
    ./test-integer3.sh

    The option -n of the command echo tells it to not print a newline after the string.

    Notice that because test and [ are treated as commands (unlike [[ and (( which are special shell constructs), each argument given to them has to be separated by a space. Also, the parentheses that group logical expressions have to be escaped like this: \( and \), otherwise shell will interpret them as something else (they have a special meaning in shell).

    Usually it is more convenient to use [[ instead of test or [.

  4. We can use the operators && (AND) and || (OR) for conditional execution of a command. They can be used like this:

    command1 && command2

    First is executed command1. If (and only if) it is successful, the command2 is executed as well.

    command1 || command2

    First is executed command1. If (and only if) it fails, the command2 is executed as well.

    For example:

    mkdir temp && cd temp
    [[ -d temp ]] || mkdir temp

    The first one is equivalent to:

    if mkdir temp; then cd temp; fi

    The second one is equivalent to:

    if [[ -d temp ]]; then : ; else mkdir temp; fi

    The command : is a null command, which means "do nothing". Without it we would get a syntax error.

Loading asciinema cast...