我一直在写一些shell脚本,如果有能力停止所述shell脚本的执行,如果任何命令失败,我会发现它很有用。请看下面的例子:

#!/bin/bash

cd some_dir

./configure --some-flags

make

make install

因此,在这种情况下,如果脚本不能更改到指定的目录,那么如果失败,它肯定不想在之后执行./configure。

现在我很清楚,我可以对每个命令进行if检查(我认为这是一个无望的解决方案),但是是否存在一个全局设置,以便在其中一个命令失败时使脚本退出?


当前回答

一个成语是:

cd some_dir && ./configure --some-flags && make && make install

我知道这可能会很长,但对于较大的脚本,您可以将其分解为逻辑函数。

其他回答

要在命令失败时立即退出脚本,请在开头添加以下内容:

set -e

这将导致脚本在不属于某些测试的命令(如if[…]]条件或&&构造)退出,退出代码为非零。

使用set -e内置:

#!/bin/bash
set -e
# Any subsequent(*) commands which fail will cause the shell script to exit immediately

或者,你可以在命令行传递-e:

bash -e my_script.sh

您也可以使用set +e禁用此行为。

你可能还想使用全部或部分的-e -u -x和-o pipefail选项,如下所示:

set -euxo pipefail

-e在错误时退出,-u在未定义的变量时退出,-o(用于选项)pipefail在命令管道失败时退出。这里详细记录了一些陷阱和变通方法。

(*)注:

命令的一部分,如果失败的命令,则shell不退出 命令列表紧跟在while或until关键字之后, 部分测试后跟if或elif保留字,部分 在&&或||列表中执行的任何命令的 在最后的&&或||之后,管道中的任何命令 最后一个,或者命令的返回值与 !

(摘自man bash)

我认为你要找的是trap命令:

trap command signal [signal ...]

欲了解更多信息,请参阅本页。

另一种选择是在脚本顶部使用set -e命令——如果任何程序/命令返回非真值,它将使脚本退出。

另一种适合第一行的可接受的答案:

#!/bin/bash -e

cd some_dir  

./configure --some-flags  

make  

make install

现有的答案中有一点没有说明如何继承错误陷阱。bash shell为使用set提供了这样一个选项

- e 如果设置了,ERR上的任何trap都将由shell函数、命令替换和在子shell环境中执行的命令继承。在这种情况下,ERR陷阱通常不会被继承。


Adam Rosenfield建议使用set -e的答案在某些情况下是正确的,但它也有自己的潜在缺陷。见GreyCat的BashFAQ - 105 -为什么不设置-e(或设置-o errexit,或陷阱ERR)做我所期望的?

根据手册,set -e退出

如果一个简单的命令以非零状态出现。如果失败的命令是紧跟着while或until关键字的命令列表的一部分,if语句中的测试的一部分,&&或||列表的一部分,除了最后一个&&或||后面的命令,管道中的任何命令,或者如果命令的返回值被颠倒!”,shell不会退出。

这意味着,set -e在以下简单情况下不起作用(详细解释可以在wiki上找到)

Using the arithmetic operator let or $((..)) ( bash 4.1 onwards) to increment a variable value as #!/usr/bin/env bash set -e i=0 let i++ # or ((i++)) on bash 4.1 or later echo "i is $i" If the offending command is not part of the last command executed via && or ||. For e.g. the below trap wouldn't fire when its expected to #!/usr/bin/env bash set -e test -d nosuchdir && echo no dir echo survived When used incorrectly in an if statement as, the exit code of the if statement is the exit code of the last executed command. In the example below the last executed command was echo which wouldn't fire the trap, even though the test -d failed #!/usr/bin/env bash set -e f() { if test -d nosuchdir; then echo no dir; fi; } f echo survived When used with command-substitution, they are ignored, unless inherit_errexit is set with bash 4.4 #!/usr/bin/env bash set -e foo=$(expr 1-1; true) echo survived when you use commands that look like assignments but aren't, such as export, declare, typeset or local. Here the function call to f will not exit as local has swept the error code that was set previously. set -e f() { local var=$(somecommand that fails); } g() { local var; var=$(somecommand that fails); } When used in a pipeline, and the offending command is not part of the last command. For e.g. the below command would still go through. One options is to enable pipefail by returning the exit code of the first failed process: set -e somecommand that fails | cat - echo survived

理想的建议是不使用set -e,而是实现自己的错误检查版本。关于在Bash脚本中引发错误的回答之一上实现自定义错误处理的更多信息