By now, you'll hopefully have a feeling for how useful if-then logic is. However, you might feel like something is missing still. If that is the case, you would be right! An if-then construct is not complete without the ELSE statement. The if-then-else construct allows us to specify what should happen if the test in the if-clause does not equal true. Semantically, it could be translated as:
We can illustrate this very easily by taking one of our earlier scripts, if-then-exit.sh, and optimizing both the flow of the script and the code:
reader@ubuntu:~/scripts/chapter_09$ cp if-then-exit.sh if-then-else.sh
reader@ubuntu:~/scripts/chapter_09$ vim if-then-else.sh
reader@ubuntu:~/scripts/chapter_09$ cat if-then-else.sh
#!/bin/bash
#####################################
# Author: Sebastiaan Tammer
# Version: v1.0.0
# Date: 2018-09-30
# Description: Use the if-then-else construct.
# Usage: ./if-then-else.sh
#####################################
FILE=/tmp/random_file.txt
# Check if the file exists.
if [[ ! -f ${FILE} ]]; then
echo "File does not exist, stopping the script!"
exit 1
else
cat ${FILE} # Print the file content.
fi
reader@ubuntu:~/scripts/chapter_09$ bash if-then-else.sh
File does not exist, stopping the script!
reader@ubuntu:~/scripts/chapter_09$ touch /tmp/random_file.txt
reader@ubuntu:~/scripts/chapter_09$ bash -x if-then-else.sh
+ FILE=/tmp/random_file.txt
+ [[ ! -f /tmp/random_file.txt ]]
+ cat /tmp/random_file.txt
Now, this is starting to look like something! We moved our cat command into the if-then-else logic block. Now, it feels (and is!) like a single command: if the file does not exist, print an error message and exit, otherwise, print its contents. It is a little weird that we used the then block for the error situation, though; by convention, that is reserved for the success condition. We can make our script a little more intuitive by swapping the then and else blocks; however, we will also need to invert our test condition. Let's take a look:
reader@ubuntu:~/scripts/chapter_09$ cp if-then-else.sh if-then-else-proper.sh
reader@ubuntu:~/scripts/chapter_09$ vim if-then-else-proper.sh
reader@ubuntu:~/scripts/chapter_09$ cat if-then-else-proper.sh
#!/bin/bash
#####################################
# Author: Sebastiaan Tammer
# Version: v1.0.0
# Date: 2018-09-30
# Description: Use the if-then-else construct, now properly.
# Usage: ./if-then-else-proper.sh file-name
#####################################
file_name=$1
# Check if the file exists.
if [[ -f ${file_name} ]]; then
cat ${file_name} # Print the file content.
else
echo "File does not exist, stopping the script!"
exit 1
fi
reader@ubuntu:~/scripts/chapter_09$ bash -x if-then-else-proper.sh /home/reader/textfile.txt
+ FILE=/home/reader/textfile.txt
+ [[ -f /home/reader/textfile.txt ]]
+ cat /home/reader/textfile.txt
Hi, this is some text.
The changes we made in this script are as follows:
- We replaced the hard-coded FILE constant with a user input variable file_name
- We removed the ! which inverts the test
- We swapped the then and else execution blocks
As it is now, the script first checks if the file exists, and if it does, it prints its contents (success scenario). If the file does not exist, the script prints an error message and exits with an exit code of 1 (failure scenario). In practice, else is often reserved for failure scenarios, and then for the success scenario. However, these are not golden rules and could differ, based on the types of test you have available. If you're ever writing a script and you want to use the else block for the success scenario, go right ahead: as long as you're sure it's the right choice for your situation, there is definitely no shame in it!
Until this point, we have only used if-then-else logic for error detection, followed by an exit 1. However, in some cases, both then and else can be used to accomplish the goal of the script, instead of one of them being used for error handling. Take a look at the following script:
reader@ubuntu:~/scripts/chapter_09$ vim empty-file.sh
reader@ubuntu:~/scripts/chapter_09$ cat empty-file.sh
#!/bin/bash
#####################################
# Author: Sebastiaan Tammer
# Version: v1.0.0
# Date: 2018-10-02
# Description: Make sure the file given as an argument is empty.
# Usage: ./empty-file.sh <file-name>
#####################################
# Grab the first argument.
file_name=$1
# If the file exists, overwrite it with the always empty file
# /dev/null; otherwise, touch it.
if [[ -f ${file_name} ]]; then
cp /dev/null ${file_name}
else
touch ${file_name}
fi
# Check if either the cp or touch worked correctly.
if [[ $? -ne 0 ]]; then
echo "Something went wrong, please check ${file_name}!"
exit 1
else
echo "Succes, file ${file_name} is now empty."
fi
reader@ubuntu:~/scripts/chapter_09$ bash -x empty-file.sh /tmp/emptyfile
+ file_name=/tmp/emptyfile
+ [[ -f /tmp/emptyfile ]]
+ touch /tmp/emptyfile
+ [[ 0 -ne 0 ]]
+ echo 'Succes, file /tmp/emptyfile is now empty.'
Succes, file /tmp/emptyfile is now empty.
reader@ubuntu:~/scripts/chapter_09$ bash -x empty-file.sh /tmp/emptyfile
+ file_name=/tmp/emptyfile
+ [[ -f /tmp/emptyfile ]]
+ cp /dev/null /tmp/emptyfile
+ [[ 0 -ne 0 ]]
+ echo 'Succes, file /tmp/emptyfile is now empty.'
Succes, file /tmp/emptyfile is now empty.
We use this script to make sure that a file exists and is empty. Basically, there are two scenarios: the file exists (and might not be empty) or it does not exist. In our if test, we check to see if the file exists. If it does, we replace it with an empty file by copying /dev/null (which is always empty) to the location given by the user. Otherwise, if the file does not exist, we simply create it using touch.
As you can see in the script's execution, the first time we run this script, the file does not exist and is created with touch. In the next run of the script, directly after, the file does exist (since it was created in the first run). This time, we can see in the debug that cp is used. Because we want to make sure whether either of these actions succeeded, we include an extra if block, which handles exit status checking, as we have seen before.