if-then-exit

As you might recall from the previous chapter, the if-then construct used by Bash is common to (almost) all programming languages. In its basic form, the idea is that you test for a condition (IF), and if that condition is true, you do something (THEN).

Here's a very basic example: if name is longer than or equal to 2 characters, then echo "hello ${name}". In this case, we assume that a name has to be, at the very least, 2 characters. If it is not, the input is invalid and we do not give it a "hello".

In the following script, if-then-exit.sh, we will see that our goal is to print the contents of a file using cat. However, before we do that, we check if the file exists, and if it doesn't, we exit the script with a message to the caller that specifies what went wrong:

reader@ubuntu:~/scripts/chapter_09$ vim if-then-exit.sh 
reader@ubuntu:~/scripts/chapter_09$ cat if-then-exit.sh
#!/bin/bash

#####################################
# Author: Sebastiaan Tammer
# Version: v1.0.0
# Date: 2018-09-30
# Description: Use the if-then-exit construct.
# Usage: ./if-then-exit.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
fi

# Print the file content.
cat ${FILE}

reader@ubuntu:~/scripts/chapter_09$ bash -x if-then-exit.sh
+ FILE=/tmp/random_file.txt
+ [[ ! -f /tmp/random_file.txt ]]
+ echo 'File does not exist, stopping the script!'
File does not exist, stopping the script!
+ exit 1

Most of this script should be clear by now. We used the extended shorthand syntax for the test, as we will do in the rest of this book. The -f flag is described in the man page of test as FILE exists and is a regular file. However, we ran into a little issue here: we want to print the file (with cat), but only if the file exists; otherwise, we want to print the message with echo. Later in this chapter, when we introduce if-then-else, we'll see how we can do this with a positive test. At the moment though, we want the test to give us a TRUE if the file we're checking is not an existing file. In this case, semantically speaking, we're doing the following: IF the file does not exist, THEN print a message and EXIT. The test syntax in Bash does not have a flag for this. There is, luckily, one powerful construct we can use: the exclamation mark, !, which negates/reverses the test!

Some examples of this are as follows:

  • if [[ -f /tmp/file ]]; then do-something -> do-something is executed if the file /tmp/file exists
  • if [[ ! -f /tmp/file ]]; then do-something -> do-something is executed if the file /tmp/file does not exist
  • if [[ -n ${variable} ]]; then do-something -> do-something is executed if the variable ${variable} is not empty
  • if [[ ! -n ${variable} ]]; then do-something -> do-something is executed if the variable ${variable} is not not empty (so, the double negative means do-something is only executed if the variable is in fact empty)
  • if [[ -z ${variable} ]]; then do-something -> do-something is executed if the variable ${variable} is empty
  • if [[ ! -z ${variable} ]]; then do-something -> do-something is executed if the variable ${variable} is not empty

As you should be aware, the last four examples overlap. This is because the flags -n (nonzero) and -z (zero) are already each other's opposites. Since we can negate the test with !, this means that -z is equal to ! -n, and ! -z is the same as -n. In this case, it would not matter if you used -n or ! -z. We would advise you to use the specific flag if it is available, before using a negation with another flag.

Let's get back to our script. When we found that the file did not exist by using the negated file exists test, we then printed the helpful message to the caller and exited the script. In this case, we never reached the cat command, but since the file does not exist anyway, the cat would never have succeeded. If we'd let the execution continue to that point, we would be presented with an error message by cat. In the case of cat, this message is no worse than our own message, but for some other commands, error messages are definitely not always as clear as we'd like; in this case, a check of our own with a clear message is not a bad thing!

Here's another example, where we use if and test to look at the status code which we'll catch in a variable:

reader@ubuntu:~/scripts/chapter_09$ vim if-then-exit-rc.sh
reader@ubuntu:~/scripts/chapter_09$ cat if-then-exit-rc.sh
#!/bin/bash

#####################################
# Author: Sebastiaan Tammer
# Version: v1.0.0
# Date: 2018-09-30
# Description: Use return codes to stop script flow.
# Usage: ./if-then-exit-rc.sh
#####################################

# Create a new top-level directory.
mkdir /temporary_dir
mkdir_rc=$?

# Test if the directory was created successfully.
if [[ ${mkdir_rc} -ne 0 ]]; then
echo "mkdir did not successfully complete, stop script execution!"
exit 1
fi

# Create a new file in our temporary directory.
touch /temporary_dir/tempfile.txt

reader@ubuntu:~/scripts/chapter_09$ bash if-then-exit-rc.sh
mkdir: cannot create directory ‘/temporary_dir’: Permission denied
mkdir did not successfully complete, stop script execution!

In the first functional part of this script, we tried to create the top-level directory /temporary_dir/. Since only root has these privileges, and we're neither running this as the root user nor with sudo, the mkdir fails. When we catch the exit status in the mkdir_rc variable, we do not know the exact value (we could print it if we wanted it), but we know one thing for sure: it is not 0, which is reserved for successful execution. So, we have two options here: we can check if the exit status is not equal to 0, or if the status code is equal to 1 (which is actually what mkdir reports back to the parent shell in this case). We generally prefer checking for the absence of success, instead of checking for a specific type of failure (as denoted by different return codes, such as 1, 113, 127, 255, and so on). If we only stop on an exit code of 1, we would continue the script in all cases where we do not get a 1: this would hopefully be a 0, but we're not sure of that. And, in general, anything that is not successful warrants stopping a script!

For this situation, checking if the return code is not 0, we're using an integer (remember, a fancy word for number) comparison. If we check man test, we can see that the -ne flag is described as INTEGER1 -ne INTEGER2: INTEGER1 is not equal to INTEGER2. So, for our logic, that would mean that, if the return code caught in the variable is not equal to 0, the command did not succeed successfully and we should stop. Remember that we could also use the -eq (equal to) flag and negate it with ! for the same effect.

In its current form, the script is a little longer than it strictly needs to be. We first store the return code in a variable, and then we compare that variable. What we can also do is directly use the exit status in the if-test construction, like so:

reader@ubuntu:~/scripts/chapter_09$ cp if-then-exit-rc.sh if-then-exit-rc-improved.sh
reader@ubuntu:~/scripts/chapter_09$ vim if-then-exit-rc-improved.sh
reader@ubuntu:~/scripts/chapter_09$ cat if-then-exit-rc-improved.sh
#!/bin/bash

#####################################
# Author: Sebastiaan Tammer
# Version: v1.0.0
# Date: 2018-09-30
# Description: Use return codes to stop script flow.
# Usage: ./if-then-exit-rc-improved.sh
#####################################

# Create a new top-level directory.
mkdir /temporary_dir

# Test if the directory was created successfully.
if [[ $? -ne 0 ]]; then
echo "mkdir did not successfully complete, stop script execution!"
exit 1
fi

# Create a new file in our temporary directory.
touch /temporary_dir/tempfile.txt

reader@ubuntu:~/scripts/chapter_09$ bash if-then-exit-rc-improved.sh
mkdir: cannot create directory ‘/temporary_dir’: Permission denied
mkdir did not successfully complete, stop script execution!

While this only saves us a single line (the variable assignment), it also saves us an unnecessary variable. You can see that we changed the test to comparing 0 to $?. We know that we want to check the execution anyway, so we might as well do it right away. Should we need to do it later, we would still need to save it in a variable, because remember: the exit status is only available directly after running a command. After that point, it has been overridden by the exit status of later commands.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.24.30