出错时自动退出bash shell脚本

rad*_*man 564 error-handling bash shell exit

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

#!/bin/bash  

cd some_dir  

./configure --some-flags  

make  

make install
Run Code Online (Sandbox Code Playgroud)

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

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

Ada*_*eld 878

使用set -e内置:

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

或者,您可以传递-e命令行:

bash -e my_script.sh
Run Code Online (Sandbox Code Playgroud)

您也可以使用禁用此行为set +e.

(*) 注意:

壳确实退出如果失败的命令是紧跟在所述命令列表的一部分直到关键字,以下测试的一部分,如果elif的保留字,在执行任何命令的一部分&&|| 列表除了最后的&&||之后的命令 ,管道中的任何命令,但是最后一个命令,或者命令的返回值是否被反转 !

(来自-e)

  • 这也是内置的Bourne Shell吗? (6认同)
  • 'Set + e'将再次恢复设置,因此您只能在错误时自动退出某些块. (6认同)
  • @Tom是:请参阅http://pubs.opengroup.org/onlinepubs/009695399/utilities/set.html#tag_04_127_03 (4认同)
  • 如果shell脚本在远程服务器上执行命令,如果任何远程命令产生错误,或者set -e`也包含在远程命令序列中,它也会中断吗? (3认同)
  • 当 `$(...)` 中的命令失败时是否有可能退出?如果我使用“set -e”并写入“false”,则 shell 退出。但它不会在 `echo $(false)` 上退出。有没有办法来解决这个问题? (2认同)
  • 请注意`set -e`是一个不稳定的功能,有时可能没有达到预期效果.请看这里有一个很好的解释:http://mywiki.wooledge.org/BashFAQ/105 (2认同)

sth*_*sth 64

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

set -e
Run Code Online (Sandbox Code Playgroud)

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


sup*_*bra 49

这是怎么做的:

#!/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 *** 
************
'
Run Code Online (Sandbox Code Playgroud)

  • 陷阱'abort'0 < - 为什么你在陷阱"0"而不是"1"? (2认同)
  • 0 - `EXIT`,退出时IOW陷阱 (2认同)
  • 请扩大这个答案。这种技术究竟如何工作? (2认同)

Myk*_*ych 36

与它一起使用pipefail.

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

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

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

第33章选项

  • 这是"set -o errexit"或"set -e". (4认同)
  • 如果有人想知道,它也适用于 Bourne Shell (`/bin/sh`) (4认同)

Pet*_*ler 22

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

#!/bin/bash -e

cd some_dir  

./configure --some-flags  

make  

make install
Run Code Online (Sandbox Code Playgroud)

  • 我以前见过这个(把'-e'放在#!行中),但是对于我来说它在Mac OS X上没有像预期的那样使用bash v3.2.57.调用/ usr/bin/false的简单测试脚本如下通过回声不要在预期时保释.使用上面接受的"set -e"工作正常. (9认同)

Mat*_*hen 20

一个成语是:

cd some_dir && ./configure --some-flags && make && make install
Run Code Online (Sandbox Code Playgroud)

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

  • 为了便于阅读,您可以在"&&"之后添加换行符 (10认同)

a_m*_*m0d 17

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

trap command signal [signal ...]
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请参阅此页面.

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


Ini*_*ian 6

现有答案中遗漏的一点是展示如何继承错误陷阱。所述bash外壳提供了一种用于在使用一种这样的选项set

-E

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


Adam Rosenfield 的答案推荐使用set -e在某些情况下是正确的,但它有其潜在的缺陷。请参阅GreyCat 的 BashFAQ - 105 - 为什么设置 -e(或设置 -o errexit,或陷阱 ERR)不按我的预期执行?

根据手册, set -e 退出

如果一个简单的命令以非零状态退出。如果失败的命令是立即下命令列表的一部分shell不会退出whileuntil关键字,部分test in a if statement,部分的&&或者||下面的列表,除了命令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" 
    
    Run Code Online (Sandbox Code Playgroud)
  2. 如果违规命令不是通过&&或执行的最后一个命令的一部分||。例如,下面的陷阱在预期时不会触发

    #!/usr/bin/env bash
    set -e
    test -d nosuchdir && echo no dir
    echo survived
    
    Run Code Online (Sandbox Code Playgroud)
  3. 当在if语句中错误使用as 时,if语句的退出代码是最后执行的命令的退出代码。在下面的示例中,最后执行的命令echo不会触发陷阱,即使test -d失败

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

    #!/usr/bin/env bash
    set -e
    foo=$(expr 1-1; true)
    echo survived
    
    Run Code Online (Sandbox Code Playgroud)
  5. 当您使用看起来像分配,但不是命令,例如exportdeclaretypesetlocal。在这里,函数调用f不会为退出local已风靡先前设定的错误代码。

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

    set -e
    somecommand that fails | cat -
    echo survived
    
    Run Code Online (Sandbox Code Playgroud)

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