我一直在编写一些shell脚本,如果能够在任何命令失败的情况下暂停执行所述shell脚本,我会发现它很有用。请参阅下面的示例:
#!/bin/bash
cd some_dir
./configure --some-flags
make
make install
因此,在这种情况下,如果脚本无法更改为指示的目录,那么如果失败,它肯定不会在之后执行./configure。
现在我很清楚我可以对每个命令进行if检查(我认为这是一个无望的解决方案),但是如果其中一个命令失败,是否有一个全局设置使脚本退出?
答案 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:导致管道返回管道中返回非零返回值的最后一个命令的退出状态。
答案 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上找到)
使用算术运算符set -e
或let
(从$((..))
开始于4.1)将变量值递增为
bash
如果有问题的命令不是通过#!/usr/bin/env bash
set -e
i=0
let i++ # or ((i++)) on bash 4.1 or later
echo "i is $i"
或&&
执行的最后命令的 not 部分。例如下面的陷阱不会在预期的时间触发
||
在#!/usr/bin/env bash
set -e
test -d nosuchdir && echo no dir
echo survived
语句中使用不正确时,if
语句的退出代码是最后执行的命令的退出代码。在下面的示例中,最后执行的命令是if
,即使echo
失败了,该命令也不会触发陷阱
test -d
与命令替换一起使用时,除非将#!/usr/bin/env bash
set -e
f() { if test -d nosuchdir; then echo no dir; fi; }
f
echo survived
设置为inherit_errexit
4.4,否则它们将被忽略
bash
,当您使用看起来像分配但不一样的命令时,例如#!/usr/bin/env bash
set -e
foo=$(expr 1-1; true)
echo survived
,export
,declare
或typeset
。在这里,local
的函数调用将不会退出,因为f
已清除了先前设置的错误代码。
local
在管道中使用时,有问题的命令不是最后一个命令的 部分。例如下面的命令仍然会通过。一种选择是通过返回 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