724

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

#!/bin/bash

cd some_dir

./configure --some-flags

make

make install

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

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

4

8 回答 8

1151

Use the set -e builtin:

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

Alternatively, you can pass -e on the command line:

bash -e my_script.sh

You can also disable this behavior with set +e.

You may also want to employ all or some of the the -e -u -x and -o pipefail options like so:

set -euxo pipefail

-e exits on error, -u errors on undefined variables, and -o (for option) pipefail exits on command pipe failures. Some gotchas and workarounds are documented well here.

(*) Note:

The shell does not exit if the command that fails is part of the command list immediately following a while or until keyword, part of the test following the if or elif reserved words, part of any command executed in a && or || list except the command following the final && or ||, any command in a pipeline but the last, or if the command's return value is being inverted with !

(from man bash)

于 2010-05-20T04:36:01.670 回答
76

To exit the script as soon as one of the commands failed, add this at the beginning:

set -e

This causes the script to exit immediately when some command that is not part of some test (like in a if [ ... ] condition or a && construct) exits with a non-zero exit code.

于 2010-05-20T04:34:26.507 回答
58

这是如何做到的:

#!/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 *** 
************
'
于 2014-03-06T12:19:33.967 回答
58

与 结合使用pipefail

set -e
set -o pipefail

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

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

第 33 章 选项

于 2016-05-18T12:43:02.757 回答
27

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

#!/bin/bash -e

cd some_dir  

./configure --some-flags  

make  

make install
于 2015-08-14T11:04:34.410 回答
23

一个成语是:

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

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

于 2010-05-20T04:26:02.270 回答
17

我认为您正在寻找的是trap命令:

trap command signal [signal ...]

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

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

于 2010-05-20T04:27:06.870 回答
9

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

-E

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


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

根据手册, set -e 退出

如果一个简单的命令以非零状态退出。如果失败的命令是紧跟在whileoruntil关键字之后的命令列表的一部分、是 的test in a if statement一部分、是&&or||列表的一部分,除了 , 之后的命令final && or ||any command in a pipeline but the last或者如果命令的返回值通过!".

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

  1. 使用算术运算符let$((..))(从bash4.1 开始)将变量值递增为

    #!/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. 当在语句中错误地使用ifas 时,该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.4inherit_errexit中设置bash

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

    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
    

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

于 2019-03-12T08:27:06.690 回答