Chapter 10: if / elif / else statements
Index
In Bash, the “if
”, “elif
”, and “else
” statements are fundamental constructs used for conditional execution of commands. These statements provide the means to make decisions within a script or in the command-line environment, enabling you to create dynamic and responsive scripts that respond to specific conditions.
In the next sections we will speak about the three statements.
The “if
” Statement
The “if
” statement is the cornerstone of conditional execution in Bash. It allows you to specify a condition or test that evaluates to either true (success) or false (failure). If the condition is true, the code within the “if
” block is executed. If it’s false, the code is skipped. The syntax of an “if
” statement typically looks like follows.
if [ CONDITION ]; then
COMMANDS;
fi
For example, you can use an “if
” statement to check if a file exists before attempting to read or manipulate it. If the condition inside the square brackets is satisfied (e.g., the file exists), the code within the “if
” block is executed.
The “elif
” Statement
The “elif
” (short for “else if”) statement is used when you have multiple conditions to check in a sequence. It follows an “if
” statement and is evaluated only if the preceding “if
” or “elif
” conditions are false. If an “elif
” condition is true, the corresponding code block is executed. The syntax of an “elif
” statement typically looks like follows.
if [ CONDITION ]; then
COMMANDS;
elif [ CONDITION2 ]; then # elif can be repeated
COMMANDS2;
fi
If the evaluation of “CONDITION
” results in “true
” then “COMMANDS
” will be executed. If not, then the next condition (“CONDITION2
”) will be evaluated and depending on the result of its evaluation “COMMANDS2
” will be executed or not.
For instance, you might use “elif
” to check various file types within a directory. If the first condition doesn’t match (e.g., it’s not a text file), the script checks the next condition (e.g., an image file).
The “else
” Statement
The “else
” statement is used in conjunction with the “if
” statement to specify a block of code that should execute when none of the preceding conditions (in the “if
” or “elif
” statements) are met. It provides a default or fallback action when all previous conditions are false. The syntax of an “else” statement typically looks like the following.
if [ CONDITION ]; then
COMMANDS;
else
COMMANDS2;
fi
In a real-world example, if you’re checking for the existence of a file and none of the preceding conditions match (the file doesn’t exist), you can use “else
” to execute an alternative action, such as creating the file.
In the case of having multiple conditions in an “if
/elif
/else
” statement that evaluates to “true
” Bash will execute that first one it finds. So yes, the order of the different conditions matters.
Also bear in mind that you can have multiple “elif
” branches as we already mentioned previously.
How to test stuff?
In Bash there are 4 ways to test for conditions. In the following subsections we will take a look at:
- “
test
” operator - “
[...]
” operator - “
[[...]]
” operator - “
((...))
” operator
In the next sub-sections we will explore each one of them. Let’s start!
“test” operator
The “test
” operator is used to evaluate conditional expressions. It’s not a built-in command of Bash. It’s in fact a separate binary[1].
The “test
” operator comes with a set of flags/operators that allow you to test for different conditions. The following table contains some of the different operators you can use with “test
”. For an exhaustive list of options, please consult the manual of the test operator[2].
The following table contains an exhaustive list of operators that you can use with “test
” and their descriptions.
Operator | Description |
---|---|
-b FILE |
FILE exists and is a block special |
-c FILE |
FILE exists and is a character special |
-d FILE |
True if FILE is a directory |
-e FILE |
True if FILE exists |
-f FILE |
FILE exists and is a regular file |
FILE1 -ef FILE2 |
FILE1 and FILE2 have the same device and inode numbers |
FILE1 -nt FILE2 |
FILE1 has a more recent modification date than FILE2 |
FILE1 -ot FILE2 |
FILE1 is older than FILE2 |
-z STRING |
True if string is empty |
-n STRING |
True if string is NOT empty |
STRING1 = STRING2 |
True if the strings are equal |
STRING1 != STRING2 |
True if the strings are not equal |
STRING1 < STRING2 |
True if STRING1 sorts before STRING2 lexicographically |
STRING1 > STRING2 |
True if STRING1 sorts after STRING2 lexicographically |
-v VAR |
True if the shell variable VAR is set |
( EXPR ) |
The parentheses are used to group expressions |
! EXPR |
True if the result of the evaluation of EXPR is false |
EXPR1 -a EXPR2 |
True if both EXPR1 and EXPR2 are true |
EXPR1 -o EXPR2 |
True if either EXPR1 or EXPR2 is true |
ARG1 OP ARG2 |
Arithmetic tests. OP is one of -eq , -ne , -lt , -le , -gt , or -ge . Arithmetic binary operators return true if ARG1 is equal, not-equal, less-than, less-than-or-equal, greater-than, or greater-than-or-equal than ARG2 |
You might be wondering what are the different arguments for the operators we provided in the previous table. They are:
FILE
: The absolute or relative path of a file or a variable that references it. For example: “/etc/profile
”.STRING
: A sequence of characters or a variable with a sequence of characters. (For example: “Hello world
”, the quotes are included).VAR
: The name of a variable. (For example: “PATH
”)EXPR
: An “expression”. This could be a combination of other operators. (For example:"TEST" = "TEST" -a "TEST1" != "TEST2"
)INT
: An integer number. (For example: 3)
In the following examples script we are going to use a few of these operators.
1 #!/usr/bin/env bash
2 #Script: if_statement.sh
3 # Declaring some variables
4 FILE_PATH="/etc/profile"
5 NUMBER_1=3
6 NUMBER_2=4
7 EMPTY=
8 # Test if the file exists
9 if test -e $FILE_PATH; then
10 echo "'$FILE_PATH' does exist"
11 fi
12 # Test if the variable is empty
13 if test -z $EMPTY; then
14 echo "The variable EMPTY has nothing"
15 fi
16 # Test if the variables are different
17 if test $FILE_PATH != "different"; then
18 echo "The values of the strings are different"
19 fi
20 # Test to compare numbers
21 if test 3 -lt 7; then
22 echo "3 is less than 7"
23 fi
24 # Combined test
25 if test $NUMBER_1 -lt $NUMBER_2 -a $FILE_PATH != "boo"; then
26 echo "Condition is true"
27 fi
When you execute the previous script you will get something like the following in your terminal.
$ ./if_statement.sh
'/etc/profile' does exist
The variable EMPTY has nothing
The values of the strings are different
3 is less than 7
Condition is true
The “test
” command has a synonym that is the “[
“ operator, which is the one we will explore next.
The “[...]
” operator (Square Brackets)
Similar to the “test
” command, the “[
“ command is a separate binary (located as well in the “/usr/bin
” folder).
The “[
“ command is equivalent to the “test
” command. This means that we can use the exact same flags and operators. The only difference with respect to the “test
” command is that the last argument should be “]
” in order to match the opening bracket.
In the next example we rewrote the script we had in the previous subsection but using the “[
“ command.
1 #!/usr/bin/env bash
2 #Script: if_statement_with_square_bracket.sh
3 # Declaring some variables
4 FILE_PATH="/etc/profile"
5 NUMBER_1=3
6 NUMBER_2=4
7 EMPTY=
8 # Test if the file exists
9 if [ -e $FILE_PATH ]; then
10 echo "'$FILE_PATH' does exist"
11 fi
12 # Test if the variable is empty
13 if [ -z $EMPTY ]; then
14 echo "The variable EMPTY has nothing"
15 fi
16 # Test if the variables are different
17 if [ $FILE_PATH != "different" ]; then
18 echo "The values of the strings are different"
19 fi
20 # Test to compare numbers
21 if [ 3 -lt 7 ]; then
22 echo "3 is less than 7"
23 fi
24 # Combined test
25 if [ $NUMBER_1 -lt $NUMBER_2 -a $FILE_PATH != "boo" ]; then
26 echo "Condition is true"
27 fi
When you execute the previous script you will see that it generates the same result as “if_statement.sh
”.
$ ./if_statement_with_square_bracket.sh
'/etc/profile' does exist
The variable EMPTY has nothing
The values of the strings are different
3 is less than 7
Condition is true
“test
” and “[...]
” commands are preferred to be used to generate a more portable code.
The “[[...]]
” operator (Double Square Brackets)
The “[[...]]
” operator is a Bash extension inspired by another shell.[3] This operator is a new, improved version of the previous “test
” and “[...]
”. On top of that is a built-in command instead of a separate binary.
The following table will give you a high-level overview of the differences between “[[...]]
” and “test
” (or “[...]
”).
Feature | [[…]] | […] |
---|---|---|
String comparison | > |
\> |
< |
\< |
|
= (or == ) |
= |
|
!= |
!= |
|
Integer comparison | -gt |
-gt |
-lt |
-lt |
|
-ge |
-ge |
|
-le |
-le |
|
-eq |
-eq |
|
-ne |
-ne |
|
Conditional evaluation | && |
-a |
|| |
-o |
|
Expression grouping | (...) |
\(...\) |
Pattern Matching | = (or == ) |
(not available) |
Regular Expression Matching | =~ |
(not available) |
Let’s see how the script we used in the last two sections would look like using the “[[...]]
” operator.
1 #!/usr/bin/env bash
2 #Script: if_statement_with_double_square_bracket.sh
3 # Declaring some variables
4 FILE_PATH="/etc/profile"
5 NUMBER_1=3
6 NUMBER_2=4
7 EMPTY=
8 # Test if the file exists
9 if [[ -e $FILE_PATH ]]; then
10 echo "'$FILE_PATH' does exist"
11 fi
12 # Test if the variable is empty
13 if [[ -z $EMPTY ]]; then
14 echo "The variable EMPTY has nothing"
15 fi
16 # Test if the variables are different
17 if [[ $FILE_PATH != "different" ]]; then
18 echo "The values of the strings are different"
19 fi
20 # Test to compare numbers
21 if [[ 3 -lt 7 ]]; then
22 echo "3 is less than 7"
23 fi
24 # Combined test
25 if [[ $NUMBER_1 -lt $NUMBER_2 && $FILE_PATH != "boo" ]]; then
26 echo "Condition is true"
27 fi
Notice that the main changes we did in the script were replacing “[...]
” with “[[...]]
” and replacing the “-a
” operator with “&&
”. When you execute the previous script you get the same result as in the two previous sections.
$ ./if_statement_with_double_square_bracket.sh
'/etc/profile' does exist
The variable EMPTY has nothing
The values of the strings are different
3 is less than 7
Condition is true
Compound command “((...))
”
In a previous chapter we talked about the compound command “((...))
”. In that chapter we used the command to declare integer variables and to modify the values of integer variables.
We also mentioned in that chapter that the “((...))
” is an actual command after all, meaning that it has a result, an exit status, that we can use.
The “((...))
” command creates an environment where you can do assignments, arithmetic operations or even several operations in the same environment (between the double parenthesis). The result of the command will be determined by the last expression that will be evaluated inside the environment.
This means that we can use the result of the “((...))
” command in conditions of the “if
/elif
” statements (and as we will see later, in loops as well).
In the following script we see a few examples with the “((...))
” command.
1 #!/usr/bin/env bash
2 #Script: if_statement_double_parentheses.sh
3 # Declaring some variables
4 FILE_PATH="/etc/profile"
5 NUMBER_1=3
6 NUMBER_2=4
7 # Combined test
8 if (( $NUMBER_1 < $NUMBER_2 )) && [[ $FILE_PATH != "boo" ]]; then
9 echo "Condition is true"
10 fi
11 # Test less than
12 if (( 3 < 7 )); then
13 echo "3 is less than 7"
14 fi
15 # Test less than or equal
16 if (( 7 <= 7 )); then
17 echo "7 is less than or equal to 7"
18 fi
19 # Test equality
20 if (( 7 == 7 )); then
21 echo "7 is equal to 7"
22 fi
23 # Test inequality
24 if (( a = 3, 8 != 7 && 7 < 10 )); then
25 echo "8 is not equal to 7 and 7 is less than 10"
26 fi
27 # Remove a file that does not exist
28 rm /tmp/does_not_exist
29 echo "Result: $?"
30 (( b = 4 ))
31 echo "Result: $?"
In the last script you can see several uses of the compound command “((...))
” by itself (lines 12, 16, 20 and 30), in combination with another operator (line 9) and using several expressions in the same environment (line 29).
If you pay attention to line 24, you will see that there are two expressions between the parenthesis. The first one is assigning the value “3
” to the variable “a
”. The second expression is evaluating a boolean condition whose result is true. This value of true will be used in the “if
” statement as a condition. As the condition is true the message in line 25 will be printed.
The last four lines (28 to 31) are showing the result code of two commands. First we try to remove a file that does not exist and, obviously, this command will fail (generating an exit status of “1
”). Second we use an assignment with the “((...))
” command which is successful, having an exit status of “0
”.
When run the script you will get the following output in your terminal.
$ ./if_statement_double_parentheses.sh
Condition is true
3 is less than 7
7 is less than or equal to 7
7 is equal to 7
8 is not equal to 7 and 7 is less than 10
rm: cannot remove '/tmp/does_not_exist': No such file or directory
Result: 1
Result: 0
Summary
In this chapter we learnt how to use the “if
” statement and its different variations (“if
”, “if-else
”, “if-elif
”, “if-elif-else
”).
We also learnt the different ways to test for conditions that will be evaluated in “if
” or “elif
”.
First we learnt about the “test
” and “[...]
” operators which are basically doing the same thing.
Then we learnt about the “[[...]]
” operator which is an improved version of the previous two operators.
Finally we learnt about the compound command “((...))
” that we use to evaluate numerical operations.
We learnt a lot in this chapter, so take your time to go over this material again if there is something that you didn’t fully get.
Remember, practice makes perfect. So? Practice, practice practice.
References
- https://linuxhint.com/bash-test-command/
- https://linuxhint.com/bash_if_else_examples/
- https://linuxize.com/post/bash-if-else-statement/
- https://stackoverflow.com/questions/2188199/how-to-use-double-or-single-brackets-parentheses-curly-braces
- https://stackoverflow.com/questions/31255699/double-parenthesis-with-and-without-dollar
- https://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO-6.html
- https://tldp.org/LDP/abs/html/dblparens.html
- https://unix.stackexchange.com/questions/306111/what-is-the-difference-between-the-bash-operators-vs-vs-vs
- https://www.baeldung.com/linux/bash-single-vs-double-brackets
1. Located usually in the “/usr/bin” folder.↩
2. Type in your terminal “man test” to know more.↩