bash shell -E选项解释中,"any trap inherited by a subshell environment"是什么意思?

In bash shell -E option explanation, what does "any trap inherited by a subshell environment" mean?

这来自 bash 设置选项的手册(set -E

-E

If set, any trap on ERR is inherited by shell functions, command substitutions, and commands executed in a subshell environment. The ERR trap is normally not inherited in such cases.

“错误陷阱”是什么意思?什么是“ERR 陷阱由子shell 环境中的shell 函数继承”? shell 脚本是否可以在陷阱和陷阱传递给子 shell 之后继续执行?有人可以用一个简单的例子详细说明吗?

trap 上的 Bash 手册说:

trap [-lp] [arg] [sigspec …]

If a sigspec is ERR, the command arg is executed whenever a pipeline (which may consist of a single simple command), a list, or a compound command returns a non-zero exit status, subject to the following conditions. The ERR trap is not executed if the failed command is part of the command list immediately following an until or while keyword, part of the test following the if or elif reserved words, part of a 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 using !. These are the same conditions obeyed by the errexit (-e) option.

你引用的 material 说 trap '…' ERR 通常不会被 shell 函数、命令替换或在 subshell 环境中执行的命令继承。但是,如果你使用set -E,那么ERR陷阱就会被继承。

句子意思是:

  • 如果设置了 set -E,那么 ERR 上的任何陷阱都会被 shell 函数继承。
  • 如果设置了 set -E,那么 ERR 上的任何陷阱都会被命令替换继承。
  • 如果设置了 set -E,那么 ERR 上的任何陷阱都会被在子 shell 环境中执行的命令继承。

'in a subshell environment' 子句不适用于 shell 函数或命令替换。

我无法解析您的问题“shell 脚本是否可以在陷阱和陷阱传递给子 shell 之后继续执行?” shell 脚本可以在陷阱之后继续执行 — 发生什么情况取决于 trap 命令的 arg 部分中的命令。

这是一个 shell 脚本,它演示了 set -E 的作用:

#!/bin/bash
#
# SO 6485-2814 'trap ERR and set -E'

trap 'echo TRAP ERR >&2' ERR

echo begin-ls
ls /non-existent
echo end-ls

func()
{
    echo begin-func
    ls /non-existent
    echo end-func
}

echo invoke-func
func
echo finish-func

echo begin-substitution
echo $(date): $(ls /non-existent)
echo end-substitution

echo begin-subshell
(ls /non-existent)
echo end-subshell

set -E

echo invoke-func
func
echo finish-func

echo begin-substitution
echo $(date): $(ls /non-existent)
echo end-substitution

echo begin-subshell
(ls /non-existent)
echo end-subshell

当运行时,它产生:

begin-ls
ls: /non-existent: No such file or directory
TRAP ERR
end-ls
invoke-func
begin-func
ls: /non-existent: No such file or directory
end-func
finish-func
begin-substitution
ls: /non-existent: No such file or directory
Sun Nov 15 23:16:25 MST 2020:
end-substitution
begin-subshell
ls: /non-existent: No such file or directory
end-subshell
invoke-func
begin-func
ls: /non-existent: No such file or directory
TRAP ERR
end-func
finish-func
begin-substitution
ls: /non-existent: No such file or directory
Sun Nov 15 23:16:25 MST 2020:
end-substitution
begin-subshell
ls: /non-existent: No such file or directory
end-subshell

如您所见,ERR 陷阱在失败的 ls 命令 运行 上触发,但在函数中 运行 时不会失败或在 subshell 中或作为(某些)命令替换的一部分。

当设置 set -E 选项时,ERR 陷阱在函数、子shell 和命令替换中的命令失败时触发。

奇怪的是,ERR 陷阱确实会在像 x=$(ls /non-existent) 这样的命令替换中触发,但不会在上面显示的更复杂的示例中触发。我不清楚这是否应该发生。 我正在使用 GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin18) — Bash (4.x) 的更高版本可能表现不一样。

Jonathan Leffler. The edit is to contain the ls executed directly (without command substitution, function call, and subshell) with the -E option set. Also, attached is the output as from execution on bash 5.0.17(1)-release. In this output, it can be seen that the TRAP fires for all four cases, namely, execution of command directly from the script, in a function, in a subshell, and in a command substitution. Whereas in the output shared by Jonathan Leffler 共享的脚本中的一个小编辑,即使设置了 -E 标志,TRAP 似乎也不会为命令替换和子 shell 触发。

剧本:

#! /bin/bash
# SO 6485-2814 'trap ERR and set -E'
trap 'echo TRAP ERR >&2' ERR

echo begin-ls
ls /non-existent
echo end-ls
echo ""

func()
{
    echo begin-func
    ls /non-existent
    echo end-func
}

echo invoke-func
func
echo finish-func
echo ""

echo begin-substitution
echo $(date): $(ls /non-existent)
echo end-substitution
echo ""

echo begin-subshell
(ls /non-existent)
echo end-subshell

echo "************ Set -E ****************"
set -E
echo begin-ls
ls /non-existent
echo end-ls
echo ""

echo invoke-func
func
echo finish-func
echo ""

echo begin-substitution
echo $(date): $(ls /non-existent)
echo end-substitution
echo ""

echo begin-subshell
(ls /non-existent)
echo end-subshell

输出: begin-ls ls: 无法访问 '/non-existent': 没有那个文件或目录 陷阱错误 end-ls

invoke-func
begin-func
ls: cannot access '/non-existent': No such file or directory
end-func
finish-func

begin-substitution
ls: cannot access '/non-existent': No such file or directory
Monday 31 January 2022 01:45:06 PM IST:
end-substitution

begin-subshell
ls: cannot access '/non-existent': No such file or directory
TRAP ERR
end-subshell
************ Set -E ****************
begin-ls
ls: cannot access '/non-existent': No such file or directory
TRAP ERR
end-ls

invoke-func
begin-func
ls: cannot access '/non-existent': No such file or directory
TRAP ERR
end-func
finish-func

begin-substitution
ls: cannot access '/non-existent': No such file or directory
TRAP ERR
Monday 31 January 2022 01:45:06 PM IST:
end-substitution

begin-subshell
ls: cannot access '/non-existent': No such file or directory
TRAP ERR
end-subshell