如何确定Bash的“立即退出”选项(set -e)被忽略。

huangapple go评论90阅读模式
英文:

How to determine when Bash's "exit immediately" option (set -e) is being ignored

问题

以下是翻译好的部分:

手册中描述了 Bash shell 的 set -e 内建选项的部分提供了以下信息:

> 如果复合命令或 shell 函数在忽略 -e 的情况下执行,那么即使 -e 被设置并且命令返回失败状态,复合命令或函数体内执行的所有命令都不会受到 -e 设置的影响。如果复合命令或 shell 函数在忽略 -e 的情况下执行时设置了 -e,那么该设置将不会在复合命令或包含函数调用的命令完成之前生效。

我在编写一个 Bash 脚本时遇到了一个问题,我在其中设置了 -e 标志,但当命令返回非零退出状态码时脚本并没有退出。这发生在一个被调用的函数内,例如:

清单 1

set -e

function foo ()
{
    set -e
    ls zzzz     # 'zzzz' 不存在
    ...   # <- 执行这些语句
}

foo
...  # <- 执行这些语句

我知道可以使用 Bash 的内建变量 $- 来显示当前设置的选项。当我在实际脚本中执行此操作时,-e 选项已设置:ehuB。然而,当被调用的函数执行返回非零退出状态的语句时,它并不会退出。因此,我假设被调用的函数必须在忽略 -e 的情况下执行。

清单 2

set -e

function foo ()
{
    set -e
    echo "settings=$-"    # ehuB
    ls zzzz     # 'zzzz' 不存在
    ...   # <- 执行这些语句
}

echo "settings=$-"    # ehuB
foo
...  # <- 执行这些语句

所以我的问题是:如何以编程方式测试确定 Bash 脚本中的代码部分是否在忽略 -e 的情况下执行?

此外,什么条件会创建一个执行上下文,在该上下文中忽略 -e 选项(当设置时)?

英文:

The manual for the Bash shell provides the following information in the section that describes the set -e builtin option:

> If a compound command or shell function executes in a context where -e is being ignored, none of the commands executed within the compound command or function body will be affected by the -e setting, even if -e is set and a command returns a failure status. If a compound command or shell function sets -e while executing in a context where -e is ignored, that setting will not have any effect until the compound command or the command containing the function call completes.

I've encountered a problem with a Bash script I'm writing where I'm setting the -e flag, but the script does not exit when a command returns a non-zero exit status code. This is occurring within a called function, e.g.,

Listing 1

set -e

function foo ()
{
    set -e
    ls zzzz     # &#39;zzzz&#39; doesn&#39;t exist
    ...   # &lt;- these statements are executed
}

foo
...  # &lt;- these statements are executed

I know I can use Bash's builtin variable $- to display the options that are currently set. When I do this in my actual script, the -e option is set: ehuB. Nevertheless, the called function does not exit when it executes a statement that returns a non-zero exit status. So, I'm assuming the called function must be executing within a context where -e is being ignored.

Listing 2

set -e

function foo ()
{
    set -e
    echo &quot;settings=$-&quot;    # ehuB
    ls zzzz     # &#39;zzzz&#39; doesn&#39;t exist
    ...   # &lt;- these statements are executed
}

echo &quot;settings=$-&quot;    # ehuB
foo
...  # &lt;- these statements are executed

So my question is this: how can I programmatically test to determine that a section of code in a Bash script is or isn't executing within a context where -e is being ignored?

Additionally, what conditions create an execution context where the -e option (when set) is ignored?

答案1

得分: 1

以下是您要翻译的内容:

what conditions create an execution context where the -e option (when set) is ignored?

在哪些情况下,执行上下文会忽略设置了 -e 选项的条件?

Is documented in:

在下列内容中有记录:

-e

如果管道返回非零状态(请参阅Pipelines),可能由单个简单命令(请参阅Simple Commands),命令列表(请参阅Lists of Commands)或复合命令(请参阅Compound Commands)组成,立即退出。如果失败的命令是在while或until关键字之后的命令列表中,if语句中的一部分,在&&或||列表中的任何命令(除了最后的&&或||之后的命令),管道中的任何命令但不是最后一个命令,或者命令的返回状态正在被!反转时,Shell不会退出。如果在忽略 -e 的情况下复合命令而不是子Shell返回非零状态,Shell不会退出。如果设置了ERR的陷阱,则在Shell退出之前会执行。

这个选项适用于Shell环境和每个子Shell环境分别(请参阅Command Execution Environment),可能会导致子Shell在执行子Shell中的所有命令之前退出。

如果复合命令或Shell函数在忽略 -e 的情况下执行,那么即使设置了 -e 并且命令返回失败状态,复合命令或函数体内执行的所有命令也不受 -e 设置的影响。如果复合命令或Shell函数在忽略 -e 的情况下执行,并且复合命令或包含函数调用的命令设置了 -e,那么该设置将在复合命令或包含函数调用的命令完成之前不会产生任何效果。

How to determine when Bash's "exit immediately" option (set -e) is being ignored

如何确定Bash的“立即退出”选项(设置 -e)何时被忽略

This is like my decision tree:

这就像我的决策树:

  • Is the command called from if, while or until? -> ignored

  • Is the command prefixed with ! ? -> ignored

  • Is the command in assignment of export, declare, typeset or local? -> ignored

  • Not ignored.

  • 命令是否从if、while或until调用?-> 被忽略

  • 命令是否以!前缀?-> 被忽略

  • 命令是否在export、declare、typeset或local的赋值中?-> 被忽略

  • 未被忽略。

Also remember about inheritance:

还要记住继承:

  • Is this an asynchronous command?
    • Is this a process substitution, like echo < <(false; echo test >&2)?
    • Is this inside a part of a pipeline, like { false; echo test >&2; } | echo?
    • Is this inside coproc?
    • then set -e is inherited
  • Are you in a subshell () ?
    • then is errexit_inherit set?

还要记住继承:

  • 这是否是异步命令?
    • 这是否是进程替代,如echo < <(false; echo test >&2)
    • 这是否在管道的一部分中,比如{ false; echo test >&2; } | echo
    • 这是否在coproc中?
    • 那么设置 -e 是继承的
  • 你是否在子Shell中()?
    • 那么 errexit_inherit 是否设置?

There is also a logic what is the actual exit status of chain of commands like.

还有一个逻辑,即命令链的实际退出状态如何。

  • in 99% of contexts, the exit status of a block of commands is the exit status of the last command executed
  • What is the exit status of a && b || c vs a || b && c?
  • Is lastpipe set?
  • What is the exit status of if block? (last command executed). Ex. set -e; ( if true; then false; fi )?

在99%的情况下,命令块的退出状态是最后一个执行的命令的退出状态。
a && b || ca || b && c 的退出状态是什么?
是否设置了lastpipe?
if块的退出状态是什么?(最后一个执行的命令)。例如:set -e; ( if true; then false; fi )

Note that I am using set -e in my scripts. I am not forcing it on other people. And there are scripts that you want to continue when one of the commands fails.

请注意,我在 我的 脚本中使用了 set -e。我不会强制其他人使用它。而且确实有一些脚本,当其中一个命令失败时,您 希望 继续执行。

how can I programmatically test to determine that a section of code in a Bash script is or isn't executing within a context where -e is being ignored?

如何通过编程方式测试以确定Bash脚本中的某个代码段是否在忽略 -e 的上下文中执行?

You can't.

你不能。

You could write a bash builtin to access exit_immediately_on_error but it is reset here https://github.com/bminor/bash/blob/ec8113b9861375e4e17b3307372569d429dec814/execute_cmd.c#L4890 before executing the builtin. Also, the checks are not really simple like https://github.com/bminor/bash/blob/ec8113b9861375e4e17b3307372569d429dec814/execute_cmd.c#L2792 so I do not see a way.

你可以编写一个bash内置命令来访问 exit_immediately_on_error,但在执行内置命令之前,它会在这里被重置:https://github.com/bminor/bash/blob/ec8113b9861375e4e17b330

英文:

> what conditions create an execution context where the -e option (when set) is ignored?

Is documented in:

> -e
>
> Exit immediately if a pipeline (see Pipelines), which may consist of a
> single simple command (see Simple Commands), a list (see Lists of
> Commands), or a compound command (see Compound Commands) returns a
> non-zero status. The shell does not exit if the command that fails is
> part of the command list immediately following a while or until
> keyword, part of the test in an if statement, part of any command
> executed in a && or || list except the command following the final &&
> or ||, any command in a pipeline but the last, or if the command’s
> return status is being inverted with !. If a compound command other
> than a subshell returns a non-zero status because a command failed
> while -e was being ignored, the shell does not exit. A trap on ERR, if
> set, is executed before the shell exits.
>
> This option applies to the shell environment and each subshell
> environment separately (see Command Execution Environment), and may
> cause subshells to exit before executing all the commands in the
> subshell.
>
> If a compound command or shell function executes in a context where -e
> is being ignored, none of the commands executed within the compound
> command or function body will be affected by the -e setting, even if
> -e is set and a command returns a failure status. If a compound command or shell function sets -e while executing in a context where
> -e is ignored, that setting will not have any effect until the compound command or the command containing the function call
> completes.

> How to determine when Bash's "exit immediately" option (set -e) is being ignored

This is like my decision tree:

  • Is the command called from if, while or until? -> ignored
  • Is the command prefixed with ! ? -> ignored
  • Is the command in assignment of export, declare, typeset or local? -> ignored
  • Not ignored.

Also remember about inheritance:

  • Is this an asynchronous command?
    • Is this a process substitution, like echo &lt; &lt;(false; echo test &gt;&amp;2)?
    • Is this inside a part of a pipeline, like { false; echo test &gt;&amp;2; } | echo?
    • Is this inside coproc?
    • then set -e is inherited
  • Are you in a subshell () ?
    • then is errexit_inherit set?

There is also a logic what is the actual exit status of chain of commands like.

  • in 99% of contexts, the exit status of a block of commands is the exit status of last command executed
  • What is the exit status of a &amp;&amp; b || c vs a || b &amp;&amp; c?
  • Is lastpipe set?
  • What is the exit status of if block? (last command executed). Ex. set -e; ( if true; then false; fi )?

Note that I am using set -e in my scripts. I am not forcing it on other people. And there are scripts that you want to continue when one of the commands fails.

> how can I programmatically test to determine that a section of code in a Bash script is or isn't executing within a context where -e is being ignored?

You can't.

You could write a bash builtin to access exit_immediately_on_error but it is reset here https://github.com/bminor/bash/blob/ec8113b9861375e4e17b3307372569d429dec814/execute_cmd.c#L4890 before executing the builtin. Also, the checks are not really simple like https://github.com/bminor/bash/blob/ec8113b9861375e4e17b3307372569d429dec814/execute_cmd.c#L2792 so I do not see a way.

答案2

得分: 1

以下是翻译好的部分:

"Mine gives result" 翻译为 "我的结果如下":

ls: cannot access 'zzzz': No such file or directory
5.1.16(1)-release
After subshell of foo

Now, going to do a direct call...
5.1.16(1)-release
ls: cannot access 'zzzz': No such file or directory
Exit code: 2

请注意,其中的特殊字符和代码部分保持原样,未进行翻译。

英文:

I could have put this in comments area, but put as an answer for formatting purposes.

Can you run following script and post results :

#!/bin/bash

set -e

trap

function foo ()
{
    set -e
    echo $BASH_VERSION
    ls zzzz     
    echo &quot;After ls&quot;
}

echo &quot;$(foo)&quot;
echo &quot;After subshell of foo&quot;

echo -e &quot;\nNow, going to do a direct call...&quot;

foo
echo &quot;After direct call of foo&quot;

Mine gives result :

ls: cannot access &#39;zzzz&#39;: No such file or directory
5.1.16(1)-release
After subshell of foo

Now, going to do a direct call...
5.1.16(1)-release
ls: cannot access &#39;zzzz&#39;: No such file or directory
Exit code : 2

huangapple
  • 本文由 发表于 2023年8月9日 15:16:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/76865438-2.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定