提问人:radman 提问时间:5/20/2010 最后编辑:Peter Mortensenradman 更新时间:2/26/2023 访问量:411382
出现错误时自动退出 Bash shell 脚本 [duplicate]
Automatic exit from Bash shell script on error [duplicate]
问:
我一直在编写一些 shell 脚本,如果有任何命令失败,如果能够停止所述 shell 脚本的执行,我会发现它很有用。有关示例,请参见下文:
#!/bin/bash
cd some_dir
./configure --some-flags
make
make install
因此,在这种情况下,如果脚本无法更改到指定的目录,那么如果失败,它肯定不想在之后执行 ./configure。
现在我很清楚我可以对每个命令进行 if 检查(我认为这是一个无望的解决方案),但是如果其中一个命令失败,是否有全局设置可以使脚本退出?
答:
一个成语是:
cd some_dir && ./configure --some-flags && make && make install
我意识到这可能会很长,但对于较大的脚本,您可以将其分解为逻辑函数。
评论
&&
我认为您要查找的是命令:trap
trap command signal [signal ...]
有关详细信息,请参阅此页面。
另一种选择是使用脚本顶部的命令 - 如果任何程序/命令返回非 true 值,它将使脚本退出。set -e
若要在其中一个命令失败后立即退出脚本,请在开头添加以下内容:
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
您可能还希望使用全部或部分 and 选项,如下所示:-e
-u
-x
-o pipefail
set -euxo pipefail
-e
出错时退出,未定义的变量出错,在执行前打印命令,并在命令管道失败时退出。这里很好地记录了一些陷阱和解决方法。-u
-x
-o (for option) pipefail
(*)注意:
如果失败的命令是 命令列表紧跟在 a while 或 until 关键字之后, 在 if 或 elif 保留字后面的部分测试中,部分 在 && 或 || 列表中执行的任何命令,命令除外 在最后的 && 或 ||之后,管道中的任何命令,但 最后一个,或者如果命令的返回值与 !
(来自man bash
)
评论
set -e
这是如何做到的:
#!/bin/sh
abort()
{
echo >&2 '
***************
*** ABORTED ***
***************
'
echo "An error occurred. Exiting..." >&2
exit 1
}
trap 'abort' 0
set -e
# Add your script below....
# If an error occurs, the abort() function will be called.
#----------------------------------------------------------
# ===> Your script goes here
# Done!
trap : 0
echo >&2 '
************
*** DONE ***
************
'
评论
EXIT
适合第一行的已接受答案的替代方案:
#!/bin/bash -e
cd some_dir
./configure --some-flags
make
make install
评论
将其与 结合使用。pipefail
set -e
set -o pipefail
-e (errexit):当命令以非零状态退出时,在出现第一个错误时中止脚本(until 或 while 循环、if-tests 和 list 构造除外)
-o pipefail:使管道返回管道中返回非零返回值的最后一个命令的退出状态。
评论
/bin/sh
)
现有答案中遗漏的一点是展示如何继承错误陷阱。shell 提供了一个这样的选项,用于使用bash
set
-E
如果设置,则 shell 函数、命令替换和在子 shell 环境中执行的命令将继承任何陷阱。在这种情况下,陷阱通常不会遗传。
ERR
ERR
亚当·罗森菲尔德(Adam Rosenfield)的答案建议在某些情况下是正确的,但它有其自身的潜在陷阱。请参阅 GreyCat 的 BashFAQ - 105 - 为什么设置 -e(或设置 -o errexit,或陷阱 ERR)没有达到我的预期?set -e
根据手册,set -e 退出
如果一个简单的命令以非零状态退出。如果失败的命令是紧跟在
while
或until
关键字之后的命令列表的一部分,if 语句中测试
的一部分,&& 或 ||
列表的一部分,但最后一个 && 或 ||'
之后的命令除外,管道中除最后一个命令之外的任何命令
,shell 不会退出,或者命令的返回值是否通过
!
反转。
这意味着,在以下简单情况下不起作用(详细解释可以在 wiki 上找到)set -e
使用算术运算符 or ( 4.1 及以后) 将变量值递增为
let
$((..))
bash
#!/usr/bin/env bash set -e i=0 let i++ # or ((i++)) on bash 4.1 or later echo "i is $i"
如果有问题的命令不是通过 或 执行的最后一个命令的一部分。例如,下面的陷阱不会在预期时触发
&&
||
#!/usr/bin/env bash set -e test -d nosuchdir && echo no dir echo survived
当在语句中使用不当时,该语句的退出代码是上次执行的命令的退出代码。在下面的示例中,最后一个执行的命令是不会触发陷阱的命令,即使失败
if
if
echo
test -d
#!/usr/bin/env bash set -e f() { if test -d nosuchdir; then echo no dir; fi; } f echo survived
当与命令替换一起使用时,它们将被忽略,除非使用 4.4 进行设置
inherit_errexit
bash
#!/usr/bin/env bash set -e foo=$(expr 1-1; true) echo survived
当您使用看起来像工作分配但实际上不是的任务的命令时,例如 、 或 。在这里,函数调用不会退出,因为已经扫描了之前设置的错误代码。
export
declare
typeset
local
f
local
set -e f() { local var=$(somecommand that fails); } g() { local var; var=$(somecommand that fails); }
在管道中使用时,并且有问题的命令不是最后一个命令的一部分。例如,以下命令仍会通过。一种选择是通过返回第一个失败进程的退出代码来启用:
pipefail
set -e somecommand that fails | cat - echo survived
理想的建议是不要使用和实现自己的错误检查版本。有关在 Bash 脚本中对我的一个答案实现自定义错误处理的详细信息set -e
评论
sh
bash