如何分析这个命令`{ 2>&3 "$@"& } 3>&2 2>/dev/null`?

7 shell bash io-redirection background-process

几周前,我看到了一个关于“ (如何)在后台静默启动任务? ”这个问题的奇怪答案。这个解决方案似乎不正确(参见我的回答),尽管 shell 似乎在后台默默地启动了任务。

I. 问题:我们真的可以重定向 shell 标准错误吗?

建议的解决方案没有任何解释,分析也没有提供可靠的答案。

在下面,您可以看到代码片段。

# Run the command given by "$@" in the background
silent_background() {
   if [[ -n $BASH_VERSION ]]; then
      { 2>&3 "$@"& } 3>&2 2>/dev/null
   fi
}
Run Code Online (Sandbox Code Playgroud)

有问题的命令是{ 2>&3 "$@"& } 3>&2 2>/dev/null.

i) 分析

命令组{ ... }( ) 为一个命令 ( )指定了两个重定向 (3>&2和)。该命令是一个具有一个重定向 ( )的异步列表( )。2>/dev/null# Run the command given by "$@" in the backgroundcmd&2>&3

POSIX 规范

每个重定向应适用于复合命令中所有未明确覆盖该重定向的命令。

标准错误重定向2>/dev/null被与异步列表关联的重定向覆盖2>&3。

具体案例

在第一种情况下,标准错误被重定向到,/dev/null而在第二种情况下,命令的标准错误仍然附加到终端。

prompt% { grep warning system.log& } 2>/dev/null
prompt% { 2>&3 grep warning system.log& } 3>&2 2>/dev/null
grep: system.log: No such file or directory
Run Code Online (Sandbox Code Playgroud)

下面,我们可以看到类似的案例。在第一种情况下,命令的标准输出仍然附加到终端。在第二种情况下,命令的标准输出重定向被修改:>echo.txt被>print.txt.

prompt% { >&3 echo some data...& } 3>&1 >echo.txt
[1] 3842
some data...
prompt% file echo.txt
echo.txt: empty
prompt% { >&3 echo some data...& } 3>print.txt >echo.txt
[1] 2765
prompt% file echo.txt
echo.txt: empty
prompt% cat print.txt
some data...
Run Code Online (Sandbox Code Playgroud)

ii) 观察

如前所述,该命令似乎在后台静默启动。更准确地说,[1] 3842不显示有关后台作业的通知,例如。

前面的分析意味着重定向是多余的,因为它们可能会被取消。

{ redir_3 cmd& } redir_1 redir_2相当于cmd&。

iii) 口译

在我看来,上述构造由于副作用而隐藏了通知。

你能解释一下这是怎么发生的吗?

二、假设

Ilkkachu 的回答和评论允许一些进展。请注意,每个进程都有自己的文件描述符。

  • 外壳消息可能会在外壳标准错误上发送。

另一个上下文:在子 shell 中执行的异步列表。该命令g不存在。

prompt% ( g& )
g: command not found
Run Code Online (Sandbox Code Playgroud)

异步命令,用括号分组的命令,...,在与 shell 环境重复的子 shell 环境中执行...

子外壳从其父外壳继承其标准错误流的值。

因此,在前一种情况下,它们的标准错误流应该是指终端。但是,不会显示作业通知,而外壳错误消息可能会显示在外壳标准错误上。

cuo*_*glm 9

您错过了最重要的一点,shell 重定向是按从左到右的顺序应用的。

在:

{ 2>&3 "$@"& } 3>&2 2>/dev/null
Run Code Online (Sandbox Code Playgroud)

整个组命令运行:

  • 文件描述符 3 => 标准错误,此时是终端。
  • 文件描述符 2(标准错误)=> /dev/null

所以当分组内的命令运行时:

  • 标准错误 => 文件描述符 3,指向终端。

因此,如果将"$@"&任何内容打印到其标准错误,则输出将打印到终端。


对于您的具体案例:

{ grep warning system.log& } 2>/dev/null
Run Code Online (Sandbox Code Playgroud)

{ grep warning system.log& }运行时标准错误指向/dev/null. grep不会覆盖任何重定向,因此其标准错误与 相同{...},并重定向到/dev/null,您没有输出到终端。

在:

{ 2>&3 grep warning system.log& } 3>&2 2>/dev/null
Run Code Online (Sandbox Code Playgroud)

grep的标准错误被重定向到文件描述符 3,它如上所述指向终端,因此您将输出到终端。


jth*_*ill 1

您错过了该{命令以 nulled 运行stderr,它使用恢复的 fd 启动包含的后台命令,但它是启动命令并发出命令启动的反馈消息的命令,并且它以 nulled 运行。