出现错误时自动退出 Bash shell 脚本 [duplicate]

Automatic exit from Bash shell script on error [duplicate]

提问人:radman 提问时间:5/20/2010 最后编辑:Peter Mortensenradman 更新时间:2/26/2023 访问量:411382

问:

我一直在编写一些 shell 脚本,如果有任何命令失败,如果能够停止所述 shell 脚本的执行,我会发现它很有用。有关示例,请参见下文:

#!/bin/bash

cd some_dir

./configure --some-flags

make

make install

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

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

bash shell 错误处理 退出

评论

2赞 radman 5/20/2010
答案是亚当关于集合 -e 的细节(这正是想要的)。还要感谢a_m0d提供有关陷阱的信息(尽管不是 100% 相关)。
2赞 William Entriken 10/23/2018
这些答案是否适用于 ?shbash

答:

23赞 Matthew Flaschen 5/20/2010 #1

一个成语是:

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

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

评论

13赞 glenn jackman 5/20/2010
为了便于阅读,您可以在 之后添加换行符&&
19赞 a_m0d 5/20/2010 #2

我认为您要查找的是命令:trap

trap command signal [signal ...]

有关详细信息,请参阅此页面

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

85赞 sth 5/20/2010 #3

若要在其中一个命令失败后立即退出脚本,请在开头添加以下内容:

set -e

这会导致脚本在不属于某些测试的命令(如在条件或构造中)以非零退出代码退出时立即退出。if [ ... ]&&

评论

0赞 dhornbein 6/13/2013
更多信息在这里:davidpashley.com/articles/...
1297赞 Adam Rosenfield 5/20/2010 #4

使用内置的 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 whileuntil 关键字之后, 在 ifelif 保留字后面的部分测试中,部分 在 &&|| 列表中执行的任何命令,命令除外 在最后的 &&||之后,管道中的任何命令,但 最后一个,或者如果命令的返回值与

(来自man bash)

评论

7赞 Tom 5/17/2012
这也是内置的 Bourne Shell 吗?
6赞 Max Nanasy 8/4/2013
@Tom 是的:见 pubs.opengroup.org/onlinepubs/009695399/utilities/...
11赞 olivervbk 4/18/2015
“Set +e”将再次恢复设置,因此您只能拥有某些在出错时自动退出的块。
3赞 user487772 5/30/2015
如果 shell 脚本在远程服务器上执行命令,如果任何远程命令会产生错误,它是否也会中断,或者是否也包含在远程命令序列中?set -e
5赞 jlh 5/25/2018
请注意,这是一个不稳定的功能,有时可能无法达到您的预期。请参阅此处的精彩解释:mywiki.wooledge.org/BashFAQ/105set -e
59赞 supercobra 3/6/2014 #5

这是如何做到的:

#!/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 *** 
************
'

评论

2赞 MAGx2 9/3/2014
trap 'abort' 0 < - 为什么你在“0”而不是“1”上做陷阱?
2赞 azat 5/23/2015
0 -- , 出口时的 IOW 陷阱EXIT
0赞 anthony 1/19/2017
如果不在 EXIT ('0') 陷阱中使用 “exit”,则 shell 将退出并保留原始退出值!
16赞 wjandrea 9/27/2017
请展开此答案。这种技术究竟是如何工作的?
31赞 Petr Peller 8/14/2015 #6

适合第一行的已接受答案的替代方案:

#!/bin/bash -e

cd some_dir  

./configure --some-flags  

make  

make install

评论

10赞 pauldoo 9/3/2015
我以前见过这个(在#!行中放置'-e'),但对我来说,它不能在Mac OS X上的bash v3.2.57上按预期工作。 调用/usr/bin/false后跟echo的简单测试脚本在预期时不会保释。使用上面接受的“set -e”就可以了。
78赞 Mike 5/18/2016 #7

将其与 结合使用。pipefail

set -e
set -o pipefail

-e (errexit):当命令以非零状态退出时,在出现第一个错误时中止脚本(untilwhile 循环、if-tests 和 list 构造除外)

-o pipefail:使管道返回管道中返回非零返回值的最后一个命令的退出状态。

第33章.选项

评论

4赞 user1457432 5/29/2017
这就是“set -o errexit”或“set -e”。
4赞 Sridhar Sarnobat 7/17/2022
如果有人想知道,它也适用于 Bourne Shell(/bin/sh)
13赞 Inian 3/12/2019 #8

现有答案中遗漏的一点是展示如何继承错误陷阱。shell 提供了一个这样的选项,用于使用bashset

-E

如果设置,则 shell 函数、命令替换和在子 shell 环境中执行的命令将继承任何陷阱。在这种情况下,陷阱通常不会遗传。ERRERR


亚当·罗森菲尔德(Adam Rosenfield)的答案建议在某些情况下是正确的,但它有其自身的潜在陷阱。请参阅 GreyCat 的 BashFAQ - 105 - 为什么设置 -e(或设置 -o errexit,或陷阱 ERR)没有达到我的预期?set -e

根据手册,set -e 退出

如果一个简单的命令以非零状态退出。如果失败的命令是紧跟在 whileuntil 关键字之后的命令列表的一部分,if 语句中测试的一部分,&& 或 || 列表的一部分,但最后一个 && 或 ||' 之后的命令除外,管道中除最后一个命令之外的任何命令,shell 不会退出,或者命令的返回值是否通过 反转。

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

  1. 使用算术运算符 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" 
    
  2. 如果有问题的命令不是通过 或 执行的最后一个命令的一部分。例如,下面的陷阱不会在预期时触发&&||

    #!/usr/bin/env bash
    set -e
    test -d nosuchdir && echo no dir
    echo survived
    
  3. 当在语句中使用不当时,该语句的退出代码是上次执行的命令的退出代码。在下面的示例中,最后一个执行的命令是不会触发陷阱的命令,即使失败ififechotest -d

    #!/usr/bin/env bash
    set -e
    f() { if test -d nosuchdir; then echo no dir; fi; }
    f 
    echo survived
    
  4. 当与命令替换一起使用时,它们将被忽略,除非使用 4.4 进行设置inherit_errexitbash

    #!/usr/bin/env bash
    set -e
    foo=$(expr 1-1; true)
    echo survived
    
  5. 当您使用看起来像工作分配但实际上不是的任务的命令时,例如 、 或 。在这里,函数调用不会退出,因为已经扫描了之前设置的错误代码。exportdeclaretypesetlocalflocal

    set -e
    f() { local var=$(somecommand that fails); }        
    g() { local var; var=$(somecommand that fails); }
    
  6. 在管道中使用时,并且有问题的命令不是最后一个命令的一部分。例如,以下命令仍会通过。一种选择是通过返回第一个失败进程的退出代码来启用:pipefail

    set -e
    somecommand that fails | cat -
    echo survived
    

理想的建议是不要使用和实现自己的错误检查版本。有关在 Bash 脚本中对我的一个答案实现自定义错误处理的详细信息set -e

评论

0赞 Warm_Duscher 1/25/2022
真的很有帮助。谢谢!