出错时自动退出bash shell脚本

时间:2010-05-20 04:21:54

标签: bash shell exit

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

#!/bin/bash  

cd some_dir  

./configure --some-flags  

make  

make install

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

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

8 个答案:

答案 0 :(得分:823)

使用内置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 禁用此行为。

您可能还想使用全部或部分-e -u -x-o pipefail选项,如下所示:

set -euxo pipefail

-e退出错误,未定义变量出现-u错误,-o (for option) pipefail出现命令管道故障。一些问题和解决方法记录良好here

(*)注意:

  

如果失败的命令是shell的一部分,那么shell会退出   紧跟 while 关键字后的命令列表,   部分是 if elif 保留字之后的部分测试   在&amp;&amp; || 列表中执行的任何命令,但命令除外   在最后的&amp;&amp; || 之后,管道中的任何命令,   最后一个,或者命令的返回值是否被反转   的 <!/强>

(来自man bash

答案 1 :(得分:62)

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

set -e

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

答案 2 :(得分:47)

以下是如何操作:

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

答案 3 :(得分:31)

pipefail一起使用。

set -e
set -o pipefail

-e(errexit):当命令以非零状态退出时,在第一次出错时中止脚本(除了在while或while循环中,if-tests,list构造除外)

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

Chapter 33. Options

答案 4 :(得分:20)

适用于第一行的已接受答案的替代方法:

#!/bin/bash -e

cd some_dir  

./configure --some-flags  

make  

make install

答案 5 :(得分:18)

一个成语是:

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

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

答案 6 :(得分:17)

我认为你要找的是trap命令:

trap command signal [signal ...]

有关详细信息,请参阅this page

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

答案 7 :(得分:0)

现有答案中遗漏的一点是显示如何继承错误陷阱。 bash外壳为使用set

的外壳提供了一个这样的选项。
  

-E

     

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


Adam Rosenfield's answer建议使用set -e在某些情况下是正确的,但它有其自身的潜在陷阱。参见GreyCat's BashFAQ - 105 - Why doesn't set -e (or set -o errexit, or trap ERR) do what I expected?

根据手册,设置-e退出

  

如果简单命令以非零状态退出。如果失败的命令紧随 while until 关键字(属于< strong> test in a if statement ,是 && || 列表的一部分,但 { {1}} final && or || ,或者如果通过 any command in a pipeline but the last 反转命令的返回值。”

这意味着!在以下简单情况下不起作用(详细说明可以在Wiki上找到)

  1. 使用算术运算符set -elet(从$((..))开始于4.1)将变量值递增为

    bash
  2. 如果有问题的命令不是通过#!/usr/bin/env bash set -e i=0 let i++ # or ((i++)) on bash 4.1 or later echo "i is $i" &&执行的最后命令的 not 部分。例如下面的陷阱不会在预期的时间触发

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

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

    bash
  5. ,当您使用看起来像分配但不一样的命令时,例如#!/usr/bin/env bash set -e foo=$(expr 1-1; true) echo survived exportdeclaretypeset。在这里,local的函数调用将不会退出,因为f已清除了先前设置的错误代码。

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

    pipefail

理想的建议是不要使用set -e somecommand that fails | cat - echo survived 并改用自己的错误检查版本。有关对Raise error in a Bash script

的答案之一实现自定义错误处理的详细信息