如果 `>()` 子 shell 是重定向的一部分(例如 `> >()`),为什么它的标准输出会不同?

如果 `>()` 子 shell 是重定向的一部分(例如 `> >()`),为什么它的标准输出会不同?

这是以下问题的后续问题:为什么`... |当 `... > >(sed 's/^/stdout: /')` 不打印时,sed 's/^/stdout: /'` 在空标准输入上打印?

更具体地说,为什么这是一个管道:

$ tee 2> >(readlink /proc/self/fd/1) < /dev/null | cat        
pipe:[17955449]

当这是终端设备时?:

$ tee >(readlink /proc/self/fd/1) < /dev/null | cat   
/dev/pts/31

我本以为前者的输出自然也是从我输入命令的 shell 继承的终端,但 bash 和 zsh 似乎都必须采取明确的步骤将其重定向到它们的命令的输出正在重定向。他们为什么要这么做?还是发生了其他事情?前者的子 shell 是从'ingtee之前的进程中生成的吗?一个子 shell 是否继承自子进程,而另一个子 shell 继承自父进程?如何?exectee

嗯...在做标签和检查时bash,我发现这两种情况都是 bash 中的管道...所以这是 zsh 的特点。

答案1

zsh 支持同一命令的多个重定向。例如,abc > def > ghi将 的完整输出放入abcdefghi。它还允许同时进行两个>和一个未经修饰的重定向,这就是您在第一个示例中使用的重定向。|

在多重重定向的情况下,进程替换为重定向到它

tee 2> >(readlink /proc/self/fd/1) < /dev/null | cat

其输出与主命令一样通过管道传输,而进程替换无需重定向到其中

tee >(readlink /proc/self/fd/1) < /dev/null | cat

没有。管道在某种意义上具有优先权:任何重定向分支的每条输出都会经过它。在这种情况下,有两个分支 - 主命令本身和进程替换的输出 - 它们都经过cat,因此都将其标准输出视为管道。

Bash 总是将替换通过管道传递到您的cat无论,并且首先不支持像这样的多个重定向。

在本质上,zsh 中具有多个重定向,仍然只能有一个|并且管道分布在重定向的所有分支上, 但流程替代本身并不是其中的一部分- 仅实际输出重定向>

这是重定向的一个属性,通过进程替换使其可见。我们可以同时使用重定向和非重定向的进程替换,并看到:

$ true >( readlink /proc/self/fd/1 ) > >( readlink /proc/self/fd/1 ) | cat
/dev/tty1
pipe:[2975]

第一个(只是替换)将 TTY 作为标准输出,第二个(重定向到)将管道输入到cat。这是构建该设置的唯一直接方法:通过管道传输到的命令的单个实例以及在其之前的管道传输和非管道传输命令的混合。如果您在不希望的情况下最终走错了轨道重定向输出,则可以使用 恢复> /dev/pts/...,但如果您确实希望像 Bash 中那样进行裸替换重定向,您仍然不走运。

进程替换本身从 shell 继承其环境,而重定向会修改输入和输出以及管道中的因素。我不认为这是必要的它是这样工作的,但有一个一致的规则:|总是分布在 上>,但忽略参数

我的实验说明了实际行为,但太长且笨拙,无法在此处包含内联,但在这个答案的修订历史中。下面我总结了四种不同的情况(是/否,>|)以及它们的行为。


案例分析

总的来说,zsh 的行为有四种不同的情况:

  1. |, 不>

    abc >( def ) | ghi
    

    这会将基本命令的输出发送到管道,将子 shell 的输出发送到 TTY,并将路径传递给abc.

  2. |,不>

    abc >( def )
    

    这会将所有内容发送到 TTY 并将路径传递到abc.

  3. 不是|,但>

    abc > >( def )
    

    这仅将基本命令的输出发送到子 shell,并将子 shell 发送到 TTY,不给 提供任何参数abc

  4. |>

    abc > >( def ) | ghi
    

    这会将基本命令的输出发送到两个都进程替换和管道,以及子 shell 到管道的输出,不给abc.它的作用就像abc | tee >( def ) | ghi.

我真的不喜欢案例 4 与案例 1 如此不同,感觉变化离管道如此之远,但事实就是如此。

相关内容