Skip to main content

1. Branching with if

The if statement has the following syntax:

if commands; then
#commands
#...
elif commands; then
#commands
#...
else
#commands
#...
fi

The elif and else parts are optional. The elif part can be repeated more than once.

  1. Commands (including the scripts and shell functions) return an exit status. By convention, an exit status of zero indicates success and any other value indicates failure.

    ls -d /usr/bin
    echo $?
    ls -d /bin/usr
    echo $?

    The builtin commands true and false do nothing except returning an exit status:

    true
    echo $?
    false
    echo $?
  2. The if statement evaluates the success or failure of the commands, based on their exit status:

    if true; then echo "It's true."; fi
    if false; then echo "It's true."; fi

    If a list of commands follows if, the last command in the list is evaluated:

    if false; true; then echo "It's true."; fi
    if true; false; then echo "It's true."; fi
  3. The command used most frequently with if is test, which performs a variety of checks and comparisons.

    touch foo.txt
    if test -e foo.txt; then echo "File exists"; fi
    if [ -e foo.txt ]; then echo "File exists"; \
    else echo "File does not exist"; fi

    The command [ is equivalent to test (it requires ] as the last argument).

    rm -f foo.txt
    if [ -e foo.txt ]; then echo "File exists"; \
    else echo "File does not exist"; fi
  4. Let's see an example script that is testing files:

    test-file.sh
    #!/bin/bash

    # test-file: Evaluate the status of a file

    FILE=~/.bashrc

    if [ -e "$FILE" ]; then
    if [ -f "$FILE" ]; then
    echo "$FILE is a regular file."
    fi
    if [ -d "$FILE" ]; then
    echo "$FILE is a directory."
    fi
    if [ -r "$FILE" ]; then
    echo "$FILE is readable."
    fi
    if [ -w "$FILE" ]; then
    echo "$FILE is writable."
    fi
    if [ -x "$FILE" ]; then
    echo "$FILE is executable/searchable."
    fi
    else
    echo "$FILE does not exist"
    exit 1
    fi

    exit
    vim test-file.sh

    Notice that the parameter $FILE is quoted within the expression. This is not required, but it is a defense against the parameter being empty or containing whitespace.

    Notice also the exit command at the end. It can optionally take a number as an argument, which becomes the exit status of the script, indicating success or failure. Without an argument, the default is the exit status of the last command executed. If the command exit is not present at all, the exit status of the script will be the exit status of the last command executed.

    ./test-file.sh

    Edit the script, change the variable FILE and execute it again. You can also try to set it to the name of a directory.

    Let's do some more testing
    sed -i test-file.sh \
    -e '/^FILE=/c FILE=./test-file.sh'
    head test-file.sh
    ./test-file.sh
    sed -i test-file.sh \
    -e '/^FILE=/c FILE=~/examples/'
    head test-file.sh
    ./test-file.sh
    sed -i test-file.sh \
    -e '/^FILE=/c FILE="non existent file"'
    head test-file.sh
    ./test-file.sh
  5. Let's see a similar example that uses a function instead:

    test-file-fun.sh
    #!/bin/bash

    FILE=~/.bashrc

    # test-file: Evaluate the status of a file
    test_file () {
    if [ -e "$FILE" ]; then
    if [ -f "$FILE" ]; then
    echo "$FILE is a regular file."
    fi
    if [ -d "$FILE" ]; then
    echo "$FILE is a directory."
    fi
    if [ -r "$FILE" ]; then
    echo "$FILE is readable."
    fi
    if [ -w "$FILE" ]; then
    echo "$FILE is writable."
    fi
    if [ -x "$FILE" ]; then
    echo "$FILE is executable/searchable."
    fi
    else
    echo "$FILE does not exist"
    return 1
    fi
    }

    test_file

    vim test-file-fun.sh

    Notice that instead of exit, a function can use return to indicate the exit status of the function.

    ./test-file-fun.sh
    Let's do some more testing
    sed -i test-file-fun.sh \
    -e '/^FILE=/c FILE=./test-file.sh'
    grep '^FILE=' test-file-fun.sh
    ./test-file-fun.sh
    sed -i test-file-fun.sh \
    -e '/^FILE=/c FILE=~/examples/'
    grep '^FILE=' test-file-fun.sh
    ./test-file-fun.sh
    sed -i test-file-fun.sh \
    -e '/^FILE=/c FILE="non existent file"'
    grep '^FILE=' test-file-fun.sh
    ./test-file-fun.sh
  6. An example with testing strings:

    test-string.sh
    #!/bin/bash

    # test-string: evaluate the value of a string

    ANSWER=maybe

    if [ -z "$ANSWER" ]; then
    echo "There is no answer." >&2
    exit 1
    fi

    if [ "$ANSWER" = "yes" ]; then
    echo "The answer is YES."
    elif [ "$ANSWER" = "no" ]; then
    echo "The answer is NO."
    elif [ "$ANSWER" = "maybe" ]; then
    echo "The answer is MAYBE."
    else
    echo "The answer is UNKNOWN."
    fi
    vim test-string.sh

    Notice that when there is an error (ANSWER is empty), we print the error message to stderr by redirecting the output of echo (>&2). We also return an exit code of 1 by exit 1.

    ./test-string.sh
    Let's do some more testing
    sed -i test-string.sh -e '/^ANSWER=/c ANSWER=yes'
    ./test-string.sh

    sed -i test-string.sh -e '/^ANSWER=/c ANSWER=no'
    ./test-string.sh

    sed -i test-string.sh -e '/^ANSWER=/c ANSWER=xyz'
    ./test-string.sh

    sed -i test-string.sh -e '/^ANSWER=/c ANSWER='
    ./test-string.sh
  7. A similar example with testing integers:

    test-integer.sh
    #!/bin/bash

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

    INT=-5

    if [ -z "$INT" ]; then
    echo "INT is empty." >&2
    exit 1
    fi

    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
    vim test-integer.sh
    ./test-integer.sh

    Change the number that is assigned to INT and execute it again.

    More testing
    sed -i test-integer.sh -e '/^INT=/c INT=11'
    ./test-integer.sh

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

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

    sed -i test-integer.sh -e '/^INT=/c INT=12'
    ./test-integer.sh
  8. For more details about the available tests let's see the help:

    help test | less
    help [
    type test
    type [
Loading asciinema cast...