出现错误时自动退出 bash shell 脚本

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2870992/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 19:17:08  来源:igfitidea点击:

Automatic exit from bash shell script on error

bashshellerror-handlingexit

提问by radman

I've been writing some shell script and I would find it useful if there was the ability to halt the execution of said shell script if any of the commands failed. See below for an example:

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

#!/bin/bash  

cd some_dir  

./configure --some-flags  

make  

make install

So in this case if the script can't change to the indicated directory then it would certainly not want to do a ./configure afterwards if it fails.

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

Now I'm well aware that I could have an if check for each command (which I think is a hopeless solution), but is there a global setting to make the script exit if one of the commands fails?

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

回答by Adam Rosenfield

Use the set -ebuiltin:

使用set -e内置:

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

Alternatively, you can pass -eon the command line:

或者,您可以通过-e命令行:

bash -e my_script.sh

You can also disablethis behavior with set +e.

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

You may also want to employ all or some of the the -e-u-xand -o pipefailoptions like so:

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

set -euxo pipefail

-eexits on error, -uerrors on undefined variables, and -o (for option) pipefailexits on command pipe failures. Some gotchas and workarounds are documented well here.

-e出错退出,-u未定义变量出错,-o (for option) pipefail命令管道失败退出。此处记录了一些问题和解决方法。

(*) Note:

(*) 笔记:

The shell does notexit if the command that fails is part of the command list immediately following a whileor untilkeyword, part of the test following the ifor elifreserved 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 !

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

(from man bash)

(来自man bash)

回答by sth

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.

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

回答by supercobra

Here is how to do it:

这是如何做到的:

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

回答by Mykhaylo Adamovych

Use it in conjunction with pipefail.

结合使用它pipefail

set -e
set -o pipefail

-e (errexit): Abort script at first error, when a command exits with non-zero status (except in until or while loops, if-tests, list constructs)

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

-o pipefail: Causes a pipeline to return the exit status of the last command in the pipe that returned a non-zero return value.

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

Chapter 33. Options

第 33 章选项

回答by Petr Peller

An alternative to the accepted answer that fits in the first line:

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

#!/bin/bash -e

cd some_dir  

./configure --some-flags  

make  

make install

回答by Matthew Flaschen

One idiom is:

一种成语是:

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

I realize that can get long, but for larger scripts you could break it into logical functions.

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

回答by a_m0d

I think that what you are looking for is the trapcommand:

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

trap command signal [signal ...]

For more information, see this page.

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

Another option is to use the set -ecommand at the top of your script - it will make the script exit if any program / command returns a non true value.

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

回答by Inian

One point missed in the existing answers is show how to inherit the error traps. The bashshell provides one such option for that using set

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

-E

If set, any trap on ERRis inherited by shell functions, command substitutions, and commands executed in a subshell environment. The ERRtrap is normally notinherited in such cases.

-E

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



Adam Rosenfield's answerrecommendation to use set -eis right in certain cases but it has its own potential pitfalls. See GreyCat's BashFAQ - 105 - Why doesn't set -e (or set -o errexit, or trap ERR) do what I expected?

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

According to the manual, set -e exits

根据手册, set -e 退出

if a simple commandexits with a non-zero status. The shell does not exit if the command that fails is part of the command list immediately following a whileor untilkeyword, part of the test in a if statement, part of an &&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 via !".

如果一个简单的命令以非零状态退出。如果失败的命令是立即下命令列表的一部分shell不会退出whileuntil关键字,部分test in a if statement,部分的&&或者||下面的列表,除了命令final && or ||any command in a pipeline but the last或者如果命令的返回值正在通过倒!”。

which means, set -edoes not work under the following simple cases (detailed explanations can be found on the wiki)

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

  1. Using the arithmetic operator letor $((..))( bash4.1 onwards) to increment a variable value as

    #!/usr/bin/env bash
    set -e
    i=0
    let i++                   # or ((i++)) on bash 4.1 or later
    echo "i is $i" 
    
  2. If the offending command is notpart of the last command executed via &&or ||. For e.g. the below trap wouldn't fire when its expected to

    #!/usr/bin/env bash
    set -e
    test -d nosuchdir && echo no dir
    echo survived
    
  3. When used incorrectly in an ifstatement as, the exit code of the ifstatement is the exit code of the last executed command. In the example below the last executed command was echowhich wouldn't fire the trap, even though the test -dfailed

    #!/usr/bin/env bash
    set -e
    f() { if test -d nosuchdir; then echo no dir; fi; }
    f 
    echo survived
    
  4. When used with command-substitution, they are ignored, unless inherit_errexitis set with bash4.4

    #!/usr/bin/env bash
    set -e
    foo=$(expr 1-1; true)
    echo survived
    
  5. when you use commands that look like assignments but aren't, such as export, declare, typesetor local. Here the function call to fwill notexit as localhas swept the error code that was set previously.

    set -e
    f() { local var=$(somecommand that fails); }        
    g() { local var; var=$(somecommand that fails); }
    
  6. When used in a pipeline, and the offending command is notpart of the last command. For e.g. the below command would still go through. One options is to enable pipefailby returning the exit code of the firstfailed process:

    set -e
    somecommand that fails | cat -
    echo survived
    
  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. 当在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
    
  4. 当与命令替换一起使用时,它们将被忽略,除非inherit_errexitbash4.4设置

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

    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
    

The ideal recommendation is to notuse set -eand implement an own version of error checking instead. More information on implementing custom error handling on one of my answers to Raise error in a Bash script

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