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
)
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)
Myk*_*ych 36
与它一起使用pipefail
.
set -e
set -o pipefail
Run Code Online (Sandbox Code Playgroud)
-e(errexit):当命令以非零状态退出时,在第一次出错时中止脚本(除了在while或while循环中,if-tests,list构造除外)
-o pipefail:使管道返回管道中返回非零返回值的最后一个命令的退出状态.
Pet*_*ler 22
适用于第一行的已接受答案的替代方案:
#!/bin/bash -e
cd some_dir
./configure --some-flags
make
make install
Run Code Online (Sandbox Code Playgroud)
Mat*_*hen 20
一个成语是:
cd some_dir && ./configure --some-flags && make && make install
Run Code Online (Sandbox Code Playgroud)
我意识到这可能会变长,但对于较大的脚本,您可以将其分解为逻辑功能.
a_m*_*m0d 17
我认为你要找的是trap
命令:
trap command signal [signal ...]
Run Code Online (Sandbox Code Playgroud)
有关更多信息,请参阅此页面.
另一种选择是使用set -e
脚本顶部的命令 - 如果任何程序/命令返回非true值,它将使脚本退出.
现有答案中遗漏的一点是展示如何继承错误陷阱。所述bash
外壳提供了一种用于在使用一种这样的选项set
-E
如果设置,任何陷阱
ERR
都会被 shell 函数、命令替换和在子 shell 环境中执行的命令继承。在这种情况下,ERR
陷阱通常不会被继承。
Adam Rosenfield 的答案推荐使用set -e
在某些情况下是正确的,但它有其潜在的缺陷。请参阅GreyCat 的 BashFAQ - 105 - 为什么设置 -e(或设置 -o errexit,或陷阱 ERR)不按我的预期执行?
根据手册, set -e 退出
如果一个简单的命令以非零状态退出。如果失败的命令是立即下命令列表的一部分shell不会退出
while
或until
关键字,部分test in a if statement
,部分的&&
或者||
下面的列表,除了命令final && or ||
,any command in a pipeline but the last
或者如果命令的返回值正在通过倒!
”。
这意味着,set -e
在以下简单情况下不起作用(详细解释可以在 wiki 上找到)
使用算术运算符let
或$((..))
(bash
4.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)
如果违规命令不是通过&&
或执行的最后一个命令的一部分||
。例如,下面的陷阱在预期时不会触发
#!/usr/bin/env bash
set -e
test -d nosuchdir && echo no dir
echo survived
Run Code Online (Sandbox Code Playgroud)
当在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)
当与命令替换一起使用时,它们将被忽略,除非inherit_errexit
用bash
4.4设置
#!/usr/bin/env bash
set -e
foo=$(expr 1-1; true)
echo survived
Run Code Online (Sandbox Code Playgroud)
当您使用看起来像分配,但不是命令,例如export
,declare
,typeset
或local
。在这里,函数调用f
将不会为退出local
已风靡先前设定的错误代码。
set -e
f() { local var=$(somecommand that fails); }
g() { local var; var=$(somecommand that fails); }
Run Code Online (Sandbox Code Playgroud)
在管道中使用时,有问题的命令不是最后一个命令的一部分。例如,下面的命令仍然会通过。一个选项是pipefail
通过返回第一个失败进程的退出代码来启用:
set -e
somecommand that fails | cat -
echo survived
Run Code Online (Sandbox Code Playgroud)
理想的建议是不要使用set -e
和实现自己的错误检查版本。有关在 Bash 脚本中引发错误的答案之一上实现自定义错误处理的更多信息