如果`>()` 子shell 是重定向的一部分(例如`> >()`),为什么它的stdout 会有所不同?

JoL*_*JoL 5 zsh process-substitution

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

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

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

当这是终端设备时?:

$ tee >(readlink /proc/self/fd/1) < /dev/null | cat   
/dev/pts/31
Run Code Online (Sandbox Code Playgroud)

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

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

Mic*_*mer 4

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

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

tee 2> >(readlink /proc/self/fd/1) < /dev/null | cat
Run Code Online (Sandbox Code Playgroud)

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

tee >(readlink /proc/self/fd/1) < /dev/null | cat
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

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

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

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


案例分析

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

  1. |, 不>

    abc >( def ) | ghi
    
    Run Code Online (Sandbox Code Playgroud)

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

  2. |,不>

    abc >( def )
    
    Run Code Online (Sandbox Code Playgroud)

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

  3. 不是|,但>

    abc > >( def )
    
    Run Code Online (Sandbox Code Playgroud)

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

  4. |>

    abc > >( def ) | ghi
    
    Run Code Online (Sandbox Code Playgroud)

    这会将基本命令的输出发送到进程替换管道,并将子 shell 的输出发送到管道,不给 提供任何参数abc。它的作用就像abc | tee >( def ) | ghi.

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