Recommended usage:

# -u variable does not exist, error
# -x Displays which line of command to execute before printing the result of the run
-e An error occurs and execution is terminated
# -o pipefail If one subcommand fails, the entire pipe command fails and the script terminates.
set -exuo pipefail
Copy the code

Document porter, original author: Ruan Yifeng teacher. Link: www.ruanyifeng.com/blog/2017/1…

The Bash script is indispensable for server development and management, and it requires a great deal of detail to master.

The set command is an important part of Bash scripts, but it is often overlooked, leading to security and maintainability problems. This article covers its basic usage so that you can feel more comfortable using Bash scripts.

A list,

As we know, when Bash executes a script, it creates a new Shell.

$ bash script.sh
Copy the code

In the above code, script.sh is executed in a new Shell. This Shell is the environment in which the script is executed, and Bash gives various parameters for this environment by default.

The set command is used to modify the operating parameters of Shell environment, that is, you can customize the environment. There are more than a dozen parameters that can be customized, and the official manual has a full list, but this article describes the four most commonly used.

By the way, if you run set directly from the command line with no arguments, all the environment variables and Shell functions are displayed.

$ set
Copy the code

Second, the set – the u

When executing a script, Bash ignores a non-existent variable by default.

#! /usr/bin/env bash
echo $a
echo bar
Copy the code

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

$ bash script.sh

bar
Copy the code

As you can see, echo $a outputs a blank line, Bash ignores the non-existent $a, and continues with echo bar. In most cases, this is not the desired behavior of the developer, and when a variable is not present, the script should report an error, not just execute it silently.

Set-u is used to change this behavior. The script adds it to the header, reports an error when it encounters a non-existent variable, and stops execution.


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

echo $a
echo bar
Copy the code

The result is as follows.

$bash script.sh bash: script.sh: line 4: a: Unbound variableCopy the code

As you can see, the script reported an error and no longer executes the following statements.

-u can also be written as -o nounset, which is equivalent.

set -o nounset
Copy the code

Third, the set – x

By default, after the script is executed, only the result is displayed on the screen. If multiple commands are executed consecutively, their results are printed consecutively. Sometimes it’s not clear what command produced a particular piece of content.

Set-x is used to print the executed command line before running the result.

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

echo bar
Copy the code

Executing the above script results in the following.

$ bash script.sh
+ echo bar
bar
Copy the code

As you can see, the command is printed before echo bar is executed, with a + at the beginning of the line. This is useful for debugging complex scripts.

There’s another way to write -x: -o xtrace.

set -o xtrace
Copy the code

Error handling of Bash

If a script contains a command that fails (with a non-zero return value), Bash by default continues to execute subsequent commands.

#! /usr/bin/env bash
foo
echo bar
Copy the code

In the above script, foo is a nonexistent command and will report an error when executed. Bash, however, ignores the error and continues to execute.

$bash script.sh script.sh: line 3: foo: command bar not foundCopy the code

As you can see, Bash simply shows an error and does not terminate execution.

This behavior is detrimental to script security and debugging. In real development, if a command fails, the script is often stopped to prevent errors from accumulating. In this case, the following notation is generally used.

command || exit 1
Copy the code

This means that the script will stop executing whenever command has a non-zero return value.

If more than one operation needs to be completed before stopping execution, use the following three notations.

# write a
command| | {echo "command failed"; exit 1; }

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

# written three
command
if [ "$?" -ne0];then echo "command failed"; exit 1; fi
Copy the code

In addition, there is a case other than stopping execution. If two commands have an inheritance relationship, the second command can be executed only after the first command succeeds.

command1 && command2
Copy the code

Five, the set – e

The above notation is somewhat troublesome and easy to neglect. Set-e fundamentally solves this problem by making the script abort whenever an error occurs.

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

foo
echo bar
Copy the code

The result is as follows:

$bash script.sh script.sh: line 4: foo: command not foundCopy the code

As you can see, the script terminates after line 4 fails.

Set -e Determines whether a command fails to run according to the returned value. However, a non-zero return value for some commands may not indicate a failure, or the developer may want the script to continue in the event of a command failure. In this case, you can temporarily disable set -e. After the command is executed, restart set -e.

set +e
command1
command2
set -e
Copy the code

In the above code, set +e means to close the **-e option and set -e means to reopen the -e** option.

Another way is to use the command | | true, make this command even if failure, the script will not terminate execution.

#! /bin/bash
set -e

foo || true
echo bar
Copy the code

In the above code, true causes this line to always execute successfully and the echo bar to execute.

There is another way to write -e **-o errexit**.

set -o errexit
Copy the code

Set-o Pipefail

One exception to set-e is that it does not apply to pipe commands.

Alleged piping, it is more child command through the pipeline operator (|) combination become a big order. Bash takes the return value of the last subcommand as the return value of the entire command. That is, the pipe command will always execute successfully as long as the last subcommand does not fail, so the command after it will still execute and set-e will be invalidated.

Take a look at the following example.

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

foo | echo a
echo bar
Copy the code

The result is as follows:

$bash script.sh a script.sh: line 4: foo: command bar was not foundCopy the code

The above code, foo is not a command, but foo | echo command is executed successfully, a the pipe lead to the back of the echo bar will continue to perform.

Set -o pipefail is used to solve this situation, as soon as one subcommand fails, the entire pipe command fails and the script terminates.

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

foo | echo a
echo bar
Copy the code

After running, the result is as follows.

$bash script.sh a script.sh: line 4: foo: command not foundCopy the code

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

The above four parameters of the set command are usually used together.

# write a
set -euxo pipefail

# write two
set -eux
set -o pipefail
Copy the code

Both are recommended at the head of all Bash scripts.

The alternative is to pass these arguments from the command line when the Bash script is executed.

$ bash -euxo pipefail script.sh
Copy the code