276

我是 shell 脚本的菜鸟。如果命令失败,我想打印一条消息并退出我的脚本。我试过了:

my_command && (echo 'my_command failed; exit)

但它不起作用。它继续执行脚本中此行之后的指令。我正在使用 Ubuntu 和 bash。

4

9 回答 9

501

尝试:

my_command || { echo 'my_command failed' ; exit 1; }

四个变化:

  • 更改&&||
  • 使用{ }代替( )
  • 介绍;之后exit
  • {前后空格}

由于您只想打印消息并仅在命令失败时退出(以非零值退出),因此您需要 a ||not an &&

cmd1 && cmd2

cmd2成功时将运行cmd1(退出值0)。然而

cmd1 || cmd2

cmd2失败时将运行cmd1(退出值非零)。

Using( )使它们内部的命令在子 shell中运行并从那里调用 aexit会导致您退出子 shell 而不是原始 shell,因此在原始 shell 中继续执行。

为了克服这种使用{ }

bash 需要最后两个更改。

于 2010-09-29T14:35:17.313 回答
160

其他答案很好地涵盖了直接问题,但您可能也有兴趣使用set -e. 这样,任何失败的命令(在特定上下文之外,如if测试)都会导致脚本中止。对于某些脚本,它非常有用。

于 2010-09-29T16:05:43.083 回答
93

如果您希望脚本中的所有命令都具有这种行为,只需添加

set -e 
set -o pipefail

在脚本的开头。这对选项告诉 bash 解释器在命令以非零退出代码返回时退出。(有关为什么pipefail需要的更多详细信息,请参阅http://petereisentraut.blogspot.com/2010/11/pipefail.html

但是,这不允许您打印退出消息。

于 2013-10-19T18:33:18.073 回答
77

另请注意,每个命令的退出状态都存储在 shell 变量 $? 中,您可以在运行命令后立即检查该变量。非零状态表示失败:

my_command
if [ $? -eq 0 ]
then
    echo "it worked"
else
    echo "it failed"
fi
于 2010-09-29T14:40:59.570 回答
17

我已经破解了以下成语:

echo "Generating from IDL..."
idlj -fclient -td java/src echo.idl
if [ $? -ne 0 ]; then { echo "Failed, aborting." ; exit 1; } fi

echo "Compiling classes..."
javac *java
if [ $? -ne 0 ]; then { echo "Failed, aborting." ; exit 1; } fi

echo "Done."

在每个命令之前加上一个信息性回显,并在每个命令之后使用同一
if [ $? -ne 0 ];...行。(当然,您可以根据需要编辑该错误消息。)

于 2012-12-17T22:58:59.137 回答
12

提供my_command是规范设计的,成功时返回 0,则&&与您想要的完全相反。你想要||

另请注意,(在 bash 中对我来说似乎不合适,但我无法从我所在的位置尝试。告诉我。

my_command || {
    echo 'my_command failed' ;
    exit 1; 
}
于 2010-09-29T14:34:45.210 回答
6

内置的trapshell 允许捕获信号和其他有用的条件,包括命令执行失败(即,非零返回状态)。因此,如果您不想显式测试每个命令的返回状态,您可以说trap "your shell code" ERR,只要命令返回非零状态,就会执行 shell 代码。例如:

trap "echo script failed; exit 1" ERR

请注意,与捕获失败命令的其他情况一样,管道需要特殊处理;以上不会抓住false | true

于 2018-03-08T21:50:32.617 回答
6

如果要保留退出错误状态,还可以使用每行一个命令的可读文件:

my_command1 || exit $?
my_command2 || exit $?

但是,这不会打印任何其他错误消息。但在某些情况下,失败的命令无论如何都会打印错误。

于 2018-03-08T14:20:13.987 回答
2

直接使用exit可能会很棘手,因为脚本可能来自其他地方(例如来自终端)。我更喜欢使用 subshel​​l set -e(加上错误应该进入 cerr,而不是 cout):

set -e
ERRCODE=0
my_command || ERRCODE=$?
test $ERRCODE == 0 ||
    (>&2 echo "My command failed ($ERRCODE)"; exit $ERRCODE)
于 2020-12-31T10:33:19.527 回答