如果命令失败,如何退出?

use*_*246 193 linux bash exit exitstatus

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

my_command && (echo 'my_command failed; exit)
Run Code Online (Sandbox Code Playgroud)

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

cod*_*ict 363

尝试:

my_command || { echo 'my_command failed' ; exit 1; }
Run Code Online (Sandbox Code Playgroud)

四个变化:

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

由于您要打印消息并仅在命令失败时退出(退出时为非零值),因此您需要a ||而不是&&.

cmd1 && cmd2
Run Code Online (Sandbox Code Playgroud)

成功cmd2时运行cmd1(退出值0).在哪里

cmd1 || cmd2
Run Code Online (Sandbox Code Playgroud)

cmd2cmd1失败时运行(退出值非零).

使用( )make命令在子shell中运行并exit从那里调用a 会导致您退出子shell而不是原始shell,因此在原始shell中继续执行.

克服这种用途 { }

bash需要最后两个更改.

  • 它似乎是相反的,但读出来它是有道理的:"执行此命令(成功)"或"打印此错误并退出" (7认同)
  • 它看起来似乎是"逆转的".如果函数"成功",则返回0,如果"失败",则返回非零,因此,当前半部分返回非零时,可能需要评估&&.这并不意味着上面的答案是错误的 - 不正确.&&和|| 脚本中的工作基于成功而不是返回值. (4认同)
  • 其背后的逻辑是该语言使用短路评估(SCE).对于SCE,f表达式的形式为"p OR q",并且p被评估为真,那么甚至没有理由看q.如果表达式的形式为"p AND q",并且p被评估为false,则没有理由查看q.原因有两个:1)它的速度更快,原因很明显; 2)它避免了某些类型的错误(例如:"如果没有SCE,"如果x!= 0 AND 10/x> 5"将崩溃) ).在这样的命令行中使用它的能力是一个愉快的副作用. (2认同)
  • 我建议回显到STDERR:`{(>&2 echo'my_command failed'); 1号出口;}` (2认同)

Dae*_*yth 119

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

  • 不幸的是,它也非常危险——`set -e` 有一套冗长而神秘的规则,关于它什么时候起作用,什么时候不起作用。(如果有人运行 `if yourfunction; then ...`,那么 `set -e` 将永远不会在 `yourfunction` 或其调用的任何东西内触发,因为该代码被视为“已检查”)。请参阅 [BashFAQ #105 的练习部分](http://mywiki.wooledge.org/BashFAQ/105#Exercises),讨论此问题和其他陷阱(其中一些仅适用于特定的 shell 版本,因此您需要确保以针对可能用于运行脚本的每个版本进行测试)。 (4认同)

Ale*_*sky 60

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

my_command
if [ $? -eq 0 ]
then
    echo "it worked"
else
    echo "it failed"
fi
Run Code Online (Sandbox Code Playgroud)

  • 这可以由if_command替换,这里不需要使用test命令. (10认同)
  • +1因为我觉得你不应该因列出这个替代方案而受到惩罚 - 它应该在桌面上 - 虽然它有点难看,而且根据我的经验太容易在两者之间运行另一个命令而没有注意到测试的性质(也许我'我只是愚蠢的). (5认同)

dam*_*ois 60

如果您希望脚本中的所有命令都具有该行为,请添加

  set -e 
  set -o pipefail
Run Code Online (Sandbox Code Playgroud)

在脚本的开头.这对选项告诉bash解释器在命令以非零退出代码返回时退出.

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

  • 您可以使用`trap` bash内置命令在退出时运行命令. (7认同)
  • 可能有趣的是解释什么命令独立而不是对.特别是因为`set -o pipefail`可能不是理想的行为.还是谢谢你的指出! (4认同)
  • `set -e` 根本不可靠、一致或可预测!为了让自己相信这一点,请查看 [BashFAQ #105](http://mywiki.wooledge.org/BashFAQ/105#Exercises) 的练习部分,或在 https://www.in 上比较不同 shell 行为的表格-ulm.de/~mascheck/various/set-e/ (4认同)

Gra*_*ier 13

我已经破解了以下习语:

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."
Run Code Online (Sandbox Code Playgroud)

在每个命令前面提供信息性回声,并使用同一
if [ $? -ne 0 ];...行跟随每个命令.(当然,如果您愿意,可以编辑该错误消息.)

  • '如果 [$? -ne 0]; 那么 ... fi' 是一种复杂的 '||' 写法 (2认同)

Ben*_*oit 10

提供my_command规范设计,成功时返回0,然后&&恰好与您想要的相反.你想要的||.

另请注意,(在bash中我似乎不对,但我不能尝试从哪里开始.告诉我.

my_command || {
    echo 'my_command failed' ;
    exit 1; 
}
Run Code Online (Sandbox Code Playgroud)

  • @Alex:如果他们分开就行了. (9认同)

小智 6

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

trap "echo script failed; exit 1" ERR

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


ale*_*ine 5

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

my_command1 || exit $?
my_command2 || exit $?
Run Code Online (Sandbox Code Playgroud)

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

  • 没有理由让它成为`exit $?`; 只是`exit` 使用`$?` 作为它的默认值。 (2认同)