我正在编写一个将从许多不同的地方调用的 shell 函数,并且我想忽略函数内部发生的所有错误,从而禁用(可能启用的)set -e
shell 选项,执行set +e
。
问题是我不知道该选项是否设置了,所以我不知道是否真的需要禁用它,更重要的是,我是否需要在最后再次设置它。
那么,我怎么知道它是否设置了?有没有办法忽略 shell 函数中所有命令的错误,忽略-e
shell 选项 if 是否设置了?
答案1
查看 $- 的值,如果它包含“e”,则设置了“-e”选项。此变量包含所有设置选项的列表。这在 bash 手册页中有描述。
答案2
从man bash
:
CONDITIONAL EXPRESSIONS
...
-o optname
True if the shell option optname is enabled.
...
这意味着,这将做到这一点:
if [ -o errexit ]
then
....
fi
并且比其他替代方案更加清晰。
PS:对于 optnames,请man bash
查找option-name
:
-o option-name
...
errexit Same as -e.
答案3
我喜欢这两个答案。Ansgar 的建议适用于 bash,如果 grep 在 PATH 中,Trevor 的建议也很好。如果你想独立于 shell (bash/sh) 和 PATH,你可以使用
[ "${-#*e}" != "$-" ]
答案4
需要考虑的三种方法:
- 用来
shopt -po errexit
将恢复所需的代码保存在变量中并在稍后调用它。 - 使用
shopt -qo errexit
which 通过退出代码告诉你打开或关闭 - 在函数内部使用子 shell 是不可能出错的,但也并不总是适用,这取决于函数的功能。
保存和恢复
如果您想将其恢复到原来的状态,有一个简单的方法可以做到:
my_func(){
local restore_errexit="$(shopt -po errexit)"
set +e
# do stuff
${restore_errexit}
}
这是因为(man bash)-p
告诉它输出将选项设置为当前选项所需的代码。
shopt [-pqsu] [-o] [optname ...]
[...] The -p option causes output to be displayed in a form that
may be reused as input
-q Suppresses normal output (quiet mode); the return status
indicates whether the optname is set or unset. [...]
-o Restricts the values of optname to be those defined
for the -o option to the set builtin.
编辑:请注意-o
手册页。如果选项是可以使用内置设置或取消设置的选项之一set
,则-o
需要。因此shopt -s extdebug
,shopt -so xtrace
控制 if 语句
您可能仍想根据它是打开还是关闭来执行其他操作,您可以[[ -o errexit ]]
按照其他答案中的建议使用,但使用shopt -q
可以与extdebug 等set
非set
选项一起使用(只是不要放“-o”):
my_func(){
local errexit_was_on
if shopt -qo errexit ; then
echo "${FUNCNAME[0]}: INFO: turning off errexit for this function"
set +o errexit # same as `set +e` or `shopt -uo errexit`
errexit_was_on=true
else
errexit_was_on=false
fi
do_stuff
if [[ ${errexit_was_on} == true ]] ; then
echo "${FUNCNAME[0]}: INFO: reactivating errexit for this function"
set -o errexit # same as `set +e` or `shopt -so errexit`
fi
}
子壳层
另一种选择是使用子 shell。这取决于你的函数的作用。如果你的函数产生输出并返回退出代码,你可以这样做,但如果它有副作用,那么这取决于:如果副作用是创建文件,没问题,如果它是设置外部范围变量,它将不起作用。
my_func(){
(
set -e
do_stuff
)
}
由于set -e
发生在子 shell 中,因此它不会影响主进程。我通常从这个开始,因为您不会出错。有了这个,我尝试通过生成输出进行通信。如果事实证明这很不方便,以至于值得通过设置外部范围变量进行通信,那么我会转向第一种方法。最后,如果我想打印一些消息,那么我会使用第二种方法。
附注:Bash 允许你这样做
my_func()(
)
这是上面相同操作的简写,这样整个函数就会在子 shell 中运行。我不建议这样做:我以前也这样做过,但有一次我开始修改一个这样的函数,却忘记了它是一个子 shell。其中一个修改是让它从外部范围设置一个变量的值。我开始相信我的电脑被鬼附身了,因为我正在设置那个变量,而调用代码看不到新值。我花了比我想承认的更多的时间来调试这个问题。