Aborting a shell script on any command fails


Overview

By utilizing Bash scripts, we can have access to a powerful programming language. Writing such scripts is an efficient way to run several commands one after the other. Even if one command fails, the others will still be executed.

We’ll learn how we can add some safeguards so that these errors don't happen. The example code has been tested in Bash. They should also work for other POSIX-compatible shell environments.

The Problem

Let’s first take a look at how Bash handles error messages by default. Let's say we have a simple shell script called "hello.sh" which prints out the word "Hello" followed by the word "World".

#!/bin/bash
echo hello
echo world

Running it gives the expected result −

$ ./hello.sh
hello
world

Next, we’ll add a statement that‘s guaranteed to fail.

#!/bin/bash
echo hello
cat non-existing-file
echo world

If executed, this script will produce an output. Execution doesn't stop, but "world" is still outputted −

$ ./hello.sh
hello
cat: non-existing-file: No such file or directory
world

We also got a 0 exit code for our script which indicates everything went well.

$ echo $?
0

Exit on First Error

If you want to make a shell script exit whenever any command within the script fails, you can use the set -e option. This option tells the shell to exit immediately if any command within the script exits with a non-zero status.

Here's an example of how you can use the set -e option in a shell script −

#!/bin/bash

# Set the -e option
set -e

# Run some commands
echo "Running command 1"
command1
echo "Running command 2"
command2
echo "Running command 3"
command3

# The script will exit if any of the commands above fail

echo "All commands completed successfully"

In this example, if any of the commands command1, command2, or command3 fail (i.e., exit with a non-zero status), the script will exit immediately and the remaining commands will not be executed.

Note that the set -e option only applies to commands that are run directly by the shell (e.g., echo, command1, etc.), not commands that are run as a result of a shell expansion (e.g., $(command)). To make the set -e option apply to all commands, including those run as a result of a shell expansion, you can use the set -o pipefail option in combination with set -e.

Using Pipefail

Unfortunately, this solution won't help if your script contains piped statements. For example, if we pipe the first two commands (the ones that fail) together, starting with the one that failed, then we get an error message.

#!/bin/bash
set -e
cat non-existing-file | echo hello
echo world

Even though we use set -e, the following command outputs −

$ ./hello.sh
hello
cat: non-existing-file: No such file or directory
world

If we want to run multiple commands without failing if any one fails, let's add -o pipefail to the first command.

#!/bin/bash
set -eo pipefail
cat non-existing-file | echo hello
echo world

We're telling bash that if an error happens inside a pipeline, it should stop and report the exit code of the last command in the pipeline.

$ ./hello.sh
hello
cat: non-existing-file: No such file or directory

This results in the same non-zero return code we've seen before −

$ echo $?
1

Conclusion

We've learned how to use the command "exit" to make shell script terminate immediately when an error occurs. We also added -o pipefail so that pipes would behave in the same way as they did before. Always add set -eo pipeline fail at the beginning of your shell script.

Updated on: 03-Jan-2023

11K+ Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements