Login With Github

10 Minutes Tutorial For The Set Command in Bash Script

The set command is an important part of the Bash script, and there will be problems with security and maintainability if you overlook it. I'll introduce its basic usage in this article for better use of Bash scripts.

1. About The set Command

We all know that when we execute a Bash script, it will create a new shell.

$ bash script.sh

In the above code, script.sh is executed in a new Shell. This new Shell is the execution environment of the script, and different parameters of the environment can be given in Bash by default.

The set command is used to modify the operating parameters of the Shell environment, which means that the environment can be customized. A total of a dozen parameters can be customized, and you can go to the official manual for the complete list. I'll introduce the four most commonly used in this article.

By the way, if there are no parameters in the command line and you run set directly, all environment variables and Shell functions will be displayed.

$ set

2. set -u

When a script is being executed, by default, Bash will ignores the variable that does not exist.

#!/usr/bin/env bash

echo $a
echo bar

In the above code, $a is a variable that does not exist. So the execution result is as follows:

$ bash script.sh

bar

As you can see, it outputs a blank line for echo $a, as $a is ignored in Bash, and then it goes on to execute echo bar. In most cases, this is not the behavior that the developer want: If the variable does not exist, the script should report an error instead of continuing executing silently.

You can use set -u to achieve the purpose. If you add it to the beginning of the script, then when it encounters a variable that does not exist, it will report an error and stop executing.

#!/usr/bin/env bash
set -u

echo $a
echo bar

The result is as follows.

$ bash script.sh
bash: script.sh:行4: a: unbound variable

As you can see, the script reports an error and doesn't execute the following statements any longer.

And -o nounset is another way to write -u. They are equivalent.

set -o nounset

3. set -x

By default, after the script is executed, it only displays the results. If multiple commands are executed continuously, the results will be output continuously. So sometimes you may wonder which command produces this piece of the result.

You can use set -x to output the executed command line before its execution result.

#!/usr/bin/env bash
set -x

echo bar

Execute the above script and the result is as follows.

$ bash script.sh
+ echo bar
bar

As you can see, before executing the echo bar command, it will print the command first, and there will be a + indicated at the beginning of the line. This is really very useful for debugging complex scripts.

And -o xtrace is the another way to write -x.

set -o xtrace

4. Error Handling in Bash

If there is a command that fails to run in the script (the returned value is not 0), Bash will continue to execute the following commands by default.

#!/usr/bin/env bash

foo
echo bar

In the above script, foo is a non-existent command and it is supposed to report an error when executed. However, Bash will ignore the error and continue to execute.

$ bash script.sh
script.sh:行3: foo: command not found
bar

As you can see, Bash shows there is an error but doesn't terminate the execution.

This behavior is not conducive for script security and debugging. In actual development, if a command fails, the script is often required to stop execution to prevent error accumulation. Thus, the following method is generally used.

command || exit 1

It means that as long as there is a non-zero return value for the command, the script will stop executing.

If you need to complete multiple operations before stopping execution, you can consider using the following three methods.

# One
command || { echo "command failed"; exit 1; }

# Two
if ! command; then echo "command failed"; exit 1; fi

# Three
command
if [ "$?" -ne 0 ]; then echo "command failed"; exit 1; fi

In addition, if there is a case that two commands have an inheritance relationship, and only if the first command succeeds can it continue to execute the second command, so you need to use the following method.

command1 && command2

5. set -e

You may feel it is a bit troublesome to write the code as above. Thus set -e can be used to solve the problem, as it will make the script execution terminate whenever an error occurs.

#!/usr/bin/env bash
set -e

foo
echo bar

The execution result is as follows.

$ bash script.sh
script.sh:行4: foo: command not found

It can be seen that the script terminates execution after the execution of line 4 fails.

The command set -e determines whether a command fails to run according to the returned value. However, some commands may have a non-zero return value that does not indicate the running fails, or the developer wants the script to continue even if the command fails. Then you can turn off set -e temporarily and turn on set -e again after the command ends.

set +e
command1
command2
set -e

In the above code, set +e means to turn off the -e option, and set -e means to turn on -e again.

Another way is to use command || true, then the script will not terminate execution even if the command fails.

#!/bin/bash
set -e

foo || true
echo bar

In the above code, true makes the execution for this statement always succeed and the echo bar executed as well.

Another way to write -e is -o errexit.

set -o errexit

6. set-o pipefail

However, set -e does not apply to pipe commands.

The so-called pipeline command is that multiple subcommands are combined into a large command by the pipeline operator (|). Bash will return the returned value of the last subcommand as the return value of the entire command. In other words, as long as the last subcommand does not fail, the pipeline command will always execute successfully, so the following command will still execute, and thus set -e will be invalid.

Let's take a look at the example below.

#!/usr/bin/env bash
set -e

foo | echo a
echo bar

The execution result is as follows.

$ bash script.sh
a
script.sh:行4: foo: command not found
bar

In the above code, foo is a non-existent command, but because the pipeline command foo | echo a can execute successfully, the following echo bar will continue to execute.

You can use set -o pipefail to solve the problem. As long as one of the subcommands fails, the entire pipeline command fails and then the script terminates execution.

#!/usr/bin/env bash
set -eo pipefail

foo | echo a
echo bar

The running result is as follows:

$ bash script.sh
a
script.sh:行4: foo: command not found

As you can see, the echo bar is not executed.

7. Summary

Generally, the above four parameters of the set command are used together.

# One
set -euxo pipefail

# Two
set -eux
set -o pipefail

The code written in these two ways is recommended to place at the head of all Bash scripts.

Alternatively, you can pass these parameters in from the command line while executing the Bash script.

$ bash -euxo pipefail script.sh

8. Reference

0 Comment

temp