我正在编写一些Shell脚本,如果有任何命令失败,我会发现停止执行该Shell脚本非常有用。以下是一个示例:
#!/bin/bash
cd some_dir
./configure --some-flags
make
make install
所以在这种情况下,如果脚本无法切换到指定的目录,那么如果它失败了,它肯定不想进行./configure。
现在我知道我可以为每个命令添加if检查(我认为这是一个绝望的解决方案),但是否有全局设置使脚本在一个命令失败时退出?
我正在编写一些Shell脚本,如果有任何命令失败,我会发现停止执行该Shell脚本非常有用。以下是一个示例:
#!/bin/bash
cd some_dir
./configure --some-flags
make
make install
所以在这种情况下,如果脚本无法切换到指定的目录,那么如果它失败了,它肯定不想进行./configure。
现在我知道我可以为每个命令添加if检查(我认为这是一个绝望的解决方案),但是否有全局设置使脚本在一个命令失败时退出?
使用内置命令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
参数在遇到未定义的变量时报错,-x
参数在执行命令前打印命令本身,-o (for option) pipefail
参数在管道命令执行失败时退出。此外,一些注意事项和解决方案在这里文档中有详细记录。
(*) 注意:
如果失败的命令是紧接着 while 或 until 关键字之后的命令列表的一部分、if 或 elif 保留字之后的测试的一部分、&& 或 || 列表中除了最后一个命令之外的任何命令、管道命令中除了最后一个命令之外的任何命令或者命令的返回值被!反转的情况下,shell不会退出。
(来自 man bash
)
set -e
在远程命令序列中? - user487772set -e
是一个不太可靠的功能,有时可能会做出您意料之外的事情。请参见这里的精彩解释:http://mywiki.wooledge.org/BashFAQ/105。 - jlh如果希望脚本在其中一个命令失败后立即退出,请在开头添加以下内容:
set -e
当某个命令(例如在if [ ... ]
条件或&&
结构中)以非零退出码退出时,这将导致脚本立即退出,而此命令不是测试的一部分。
在使用时要与pipefail
一起使用。
set -e
set -o pipefail
-e (errexit): 当命令以非零状态退出时(除了 until 或 while 循环、if-tests 和列表结构),在第一个错误处中止脚本执行。
-o pipefail: 导致管道返回最后一个返回非零值的命令的退出状态。
/bin/sh
)。 - Sridhar Sarnobat以下是如何操作的步骤:
#!/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 ***
************
'
退出
,即在退出时陷入IOW - azat一个与被接受答案相比,适合放在第一行的替代方案:
#!/bin/bash -e
cd some_dir
./configure --some-flags
make
make install
一个常用语是:
cd some_dir && ./configure --some-flags && make && make install
我意识到这可能会很长,但对于更大的脚本,您可以将其分解为逻辑函数。
我认为你要找的是 trap
命令:
trap command signal [signal ...]
更多信息请参见此页面。set -e
命令 - 它将使脚本在任何程序/命令返回非真值时退出。set
,bash
shell提供了一种选项来实现这一点。
-E
如果设置了此选项,则任何对
ERR
的陷阱都将被shell函数、命令替换和在子shell环境中执行的命令所继承。通常,在这些情况下不会继承ERR
陷阱。
Adam Rosenfield的答案建议在某些情况下使用set -e
是正确的,但它也有潜在的缺陷。请参见GreyCat's BashFAQ - 105 - Why doesn't set -e (or set -o errexit, or trap ERR) do what I expected?。
根据手册,如果一个简单命令以非零状态退出,则set -e
会退出。
如果一个简单命令以非零状态退出,则shell不会退出。如果失败的命令是紧随
while
或until
关键字之后的命令列表的一部分,是if语句中的测试
的一部分,是&&或||
列表的一部分,除了最终的&&或||
之后的命令,管道中除了最后一个命令
之外的任何命令,或者如果通过!
来反转命令的返回值,则shell不会退出。
这意味着,在以下简单情况下,set -e
不起作用(详细解释可以在wiki上找到)
Using the arithmetic operator let
or $((..))
( bash
4.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"
If the offending command is not part 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
When used incorrectly in an if
statement as, the exit code of the if
statement is the exit code of the last executed command. In the example below the last executed command was echo
which wouldn't fire the trap, even though the test -d
failed
#!/usr/bin/env bash
set -e
f() { if test -d nosuchdir; then echo no dir; fi; }
f
echo survived
When used with command-substitution, they are ignored, unless inherit_errexit
is set with bash
4.4
#!/usr/bin/env bash
set -e
foo=$(expr 1-1; true)
echo survived
when you use commands that look like assignments but aren't, such as export
, declare
, typeset
or local
. Here the function call to f
will not exit as local
has swept the error code that was set previously.
set -e
f() { local var=$(somecommand that fails); }
g() { local var; var=$(somecommand that fails); }
When used in a pipeline, and the offending command is not part of the last command. For e.g. the below command would still go through. One options is to enable pipefail
by returning the exit code of the first failed process:
set -e
somecommand that fails | cat -
echo survived
set -e
,而是实现自己的错误检查版本。有关在我的一个答案中实现自定义错误处理的更多信息,请参见Raise error in a Bash script。
sh
和bash
? - William Entriken