Conditional Expressions in the Bash Shell

Niraj Menon Jan 04, 2022
  1. the if Statement in Bash
  2. Built-In Bash Conditionals
Conditional Expressions in the Bash Shell

Bash as a programming language is not most programmers’ go-to, especially with far more common languages like Python and C.

It’s helpful to write programming constructs to execute commands based on criteria with if statements or the same command with multiple arguments using a for loop.

This tutorial will go over if statements in Bash, focusing on the kinds of conditional expressions used in if statements.

the if Statement in Bash

The general syntax of the if statement in Bash is as follows.

if condition; then
    do-if-true;
elif second-condition; then
    do-else-if-true
elif third-condition; then
    do-else-if-third-true
else
    do-else-false
fi

The conditions themselves can have different syntax, depending on the required condition.

  • If you would like to use the output of a command as a condition, such as grep the contents of a file to see if it contains a certain substring, you can place the command after the if keyword, as follows.
if greq -q "string" file.txt; then
    echo "string exists in file.txt."
else
    echo "string does not exist in file.txt."
fi
  • [ "$s" = "string" ] checks if the variable $s contains the value string. It is also equivalent to test "$s" = "string", where test effectively substitutes the square brackets.
if [ "$s" = "string" ]; then
    echo "string is equivalent to \$s"
else
    echo "string is not equivalent to \$s"
fi
  • You can even perform arithmetic operations, such as checking if the expected number of files or lines in a file is certain or if the number in a directory is smaller or larger than that of another directory.

    We do this with pairs of parentheses.

a=$(ls dir_a | wc -l)
b=$(ls dir_b | wc -l)
if (( a < b )); then
    echo "Directory a has fewer files than directory b."
elif (( a > b )); then
    echo "Directory a has more files than directory b."
else
    echo "Directory a has the same number of files as that of directory b."
fi

Built-In Bash Conditionals

Sometimes, running a separate program to check something as simple as file existence, modification dates, and sizes is tedious and resource-consuming, so Bash can check such attributes as if statements directly.

The following flags check for these attributes’ existence or non-existence, returning an effectively true or false result to the if statement.

For example, to check if a command produced no output, we can treat its output as a string and check if the length of that string is zero with the -z flag, as follows.

out=$(bash print-if-error.sh)
if [[ -z $out ]]; then
    echo "The script ran successfully."
else
    echo "The script produced the following errors: "
    echo $out
fi

To get the inverse of the condition, you can either use the ! NOT operator, or use the converse form of this flag, which is either -n, or no flag at all, since a non-empty string can behave as “true” in a Bash if statement.

out=$(bash print-if-error.sh)
if [[ $out ]]; then
    echo "The script produced the following errors: "
    echo $out
else
    echo "The script ran successfully."
fi

Some other useful flags are explained below, with explanations referenced from the GNU Bash Reference Manual.

  1. -f checks if the file exists and it’s a regular file.
  2. -d checks if the argument provided was a directory.
  3. -h checks if the argument provided was a symbolic link.
  4. -s checks if the file exists and is not empty.
  5. -r check if the file is readable.
  6. -w check if the file is writable.
  7. -x check if the file is executable.

For flags that take two arguments, like comparison operators, the first argument comes before the flag while the second appears after the flag.

Suppose we compare two files - a.txt and b.txt - to see which is newer, with the -nt flag. The converse will check if it is older, with -ot.

out=$(bash print-if-error.sh)
if [[ a.txt -nt b.txt ]]; then
    echo "a.txt is newer than b.txt."
else
    echo "a.txt is older or the same age as b.txt."
fi

Other such two-operand flags are as follows.

  1. -ef ensures that the two arguments refer to the same device and inode numbers.
  2. != ensures that the two arguments are not equal.
  3. < checks that the first argument is alphabetically before the second.
  4. > checks that the first argument is alphabetically after the second.
  5. -eq assumes that both args are integers and checks that they are equal.
  6. -ne assumes that both args are integers and checks that they are not equal.
  7. -lt assumes that both args are integers and checks that the first is less than the second. One can use -le to check if they are equal.
  8. -gt assumes that both args are integers and checks that the first is greater than the second. One can use -ge to check if they are equal.

Related Article - Bash Condition