Contents

如果任何命令失败,中止Shell脚本

1.概述

使用Bash 脚本 ,我们拥有了一个强大的工具。Bash 脚本是连续运行多个命令的好方法。在编写脚本的时候,我们要提醒自己,如果一个命令失败了,后面的所有命令仍然会执行。在本文中,我们将学习如何通过添加一些保护措施来防止这种情况。本文示例中使用的命令已经在 Bash 中进行了测试。它们也应该在其他 POSIX 兼容的 shell 中工作。

2. 问题

首先,让我们看一下 bash 脚本在默认情况下是如何处理错误的。假设我们有一个简单的脚本hello.sh打印单词 ‘hello’ 和 ‘world’:

#!/bin/bash
echo hello
echo world

运行它会给出预期的结果:

$ ./hello.sh
hello
world

现在让我们添加一个保证失败的语句:

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

执行时,此脚本将打印错误。然而,执行并没有停止,‘world’ 仍然被打印出来:

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

此外,我们的脚本的退出代码为零,表明一切正常:

$ echo $?
0

3. 第一个错误退出

假设我们想让我们的脚本在发生第一个错误时以非零退出代码终止。因此,我们必须在脚本开头使用set 更改 shell 的默认行为:

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

在第一行使用 set -e,我们告诉 Bash 在第一个错误时停止执行:

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

此外,返回的退出代码等于失败命令的退出代码:

$ echo $?
1

4. 使用pipefail

不幸的是,此解决方案不适用于包含管道语句 的脚本。例如,让我们将前两个命令连接在一起,从失败的命令开始:

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

尽管我们使用 了set -e,但输出是:

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

为了处理这种情况,让我们在第一行的set命令中添加*-o pipefail* 作为附加选项 :

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

因此我们告诉 Bash,当管道中发生错误时,它应该停止并返回最右边失败的命令的退出代码:

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

这会产生与我们之前看到的相同的非零退出代码:

$ echo $?
1