我如何知道 set -e 选项是否启用?

我如何知道 set -e 选项是否启用?

我正在编写一个将从许多不同的地方调用的 shell 函数,并且我想忽略函数内部发生的所有错误,从而禁用(可能启用的)set -eshell 选项,执行set +e

问题是我不知道该选项是否设置了,所以我不知道是否真的需要禁用它,更重要的是,我是否需要在最后再次设置它。

那么,我怎么知道它是否设置了?有没有办法忽略 shell 函数中所有命令的错误,忽略-eshell 选项 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 errexitwhich 通过退出代码告诉你打开或关闭
  • 在函数内部使用子 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 extdebugshopt -so xtrace

控制 if 语句

您可能仍想根据它是打开还是关闭来执行其他操作,您可以[[ -o errexit ]]按照其他答案中的建议使用,但使用shopt -q可以与extdebug 等setset选项一起使用(只是不要放“-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。其中一个修改是让它从外部范围设置一个变量的值。我开始相信我的电脑被鬼附身了,因为我正在设置那个变量,而调用代码看不到新值。我花了比我想承认的更多的时间来调试这个问题。

相关内容