Bash 为什么“>;”重定向不捕获替换的进程';史都?

Bash 为什么“>;”重定向不捕获替换的进程';史都?,bash,output-redirect,process-substitution,Bash,Output Redirect,Process Substitution,在回答一个关于管道和重定向的问题时,robert提到管道还捕获了管道中替代流程的标准,而重定向则没有。为什么会这样?到底是什么导致了这种行为: bash-4.1$ echo -e '1\n2' | tee >(head -n1) >redirect 1 bash-4.1$ cat redirect 1 2 bash-4.1$ echo -e '1\n2' | tee >(head -n1) | cat >pipe bash-4.1$ cat pipe 1 2 1 我本以

在回答一个关于管道和重定向的问题时,robert提到管道还捕获了管道中替代流程的标准,而重定向则没有。为什么会这样?到底是什么导致了这种行为:

bash-4.1$ echo -e '1\n2' | tee >(head -n1) >redirect
1
bash-4.1$ cat redirect
1
2
bash-4.1$ echo -e '1\n2' | tee >(head -n1) | cat >pipe
bash-4.1$ cat pipe
1
2
1
我本以为两种形式都会产生相同的结果——后者

阅读一个不同的问题,在命令中重新排序重定向可能会产生所需的结果,这似乎是合理的,但无论顺序如何,结果总是相同的:

bash-4.1$ echo -e '1\n2' | tee >redirect >(head -n1)
1
bash-4.1$ cat redirect
1
2
bash-4.1$ echo -e '1\n2' | >redirect tee >(head -n1)
1
bash-4.1$ cat redirect
1
2
为什么stdout重定向只影响
tee
,而pipe也会捕获被替换的进程
head
?仅仅是“设计的”


与上述问题相关的一个想法是:我认为重定向到文件并管道化输出永远不会有意义,但在进程替换中确实有意义:

bash-4.1$ echo -e '1\n2\n3' | tee >(head -n1) >(tail -n1) >tee_out | cat >subst_out
bash-4.1$ cat tee_out
1
2
3
bash-4.1$ cat subst_out
1
3

运行
head
的shell由运行
tee
的同一个shell生成,这意味着
tee
head
都继承相同的标准输出文件描述符,该文件描述符连接到管道
cat
。这意味着
tee
head
都将其输出通过管道传输到
cat
,从而产生您看到的行为。

echo -e '1\n2' | tee >(head -n1) > redirect
,在
|
之后,只有
tee
的标准输出被重定向到文件,
head
仍然输出到tty。要重定向
tee
head
的标准输出,您可以编写

echo -e '1\n2' | { tee >(head -n1); } > redirect

为了


tee>(head-n1)
作为一个整体,它们的标准液通过管道输送到
cat
。它在逻辑上与echo-e'1\n2'{tee>(head-n1)}>重定向TL;DR:当执行部分管道时,shell首先执行stdin/stdout的管道重定向,然后执行
>//(echo y)>文件时,它首先分叉一次以执行复杂命令(在某些情况下可以避免,如内置),然后分叉的shell:

  • 创建
    管道
    (用于流程替换)
  • 第二个回声的叉子
  • 分叉:将其
    stdin
    连接到
    管道[1]
  • fork:
    exec
    echo y
    exec
    'ed
    echo
    继承:
    • 标准DIN连接到管道[1]
    • 不变标准
  • 打开
    文件
  • 将其
    标准输出连接到
    文件
  • exec
    echo x/proc//fd/
    exec
    'ed
    echo
    继承:
    • 标准数据保持不变
    • 标准输出已连接到
      文件
  • 这里,第二个echo继承分叉shell的
    stdout
    ,然后分叉shell将其
    stdout
    重定向到
    文件。在这种情况下,我认为这种行动顺序没有绝对必要,但我认为这样做更有意义

    管道重定向 当shell遇到
    echo x>(echo y)| cat>文件时,它检测到一个管道并开始处理它(无分叉):

  • 父级:创建一个
    管道
    (对应于完整命令中唯一实际的
    |
  • 父级:
    管道左侧的叉子
    
  • fork1:将其
    标准输出连接到
    管道[0]
  • fork1:创建一个
    管道子节点
    (用于流程替换)
  • fork1:第二个echo的forks
  • 嵌套分叉:将其
    stdin
    连接到
    pipe\u subst[1]
  • 嵌套的fork:
    exec
    echo y
    exec
    'ed
    echo
    继承:
    • 标准DIN从内叉连接到管道接头[1]
  • 标准输出从外叉连接到管道[0]
  • fork1:
    exec
    echo x/proc//fd/
    exec
    'ed
    echo
    继承:
    • 标准数据保持不变
    • 标准输出连接到
      管道[0]
  • 父项:
    管道右侧的分叉(这个分叉有时也可以避免)
    
  • fork2:将其
    stdin
    连接到
    管道[1]
  • fork2:打开
    文件
  • fork2:将其
    标准输出连接到
    文件
  • fork2:
    exec
    cat
    exec
    'ed
    cat
    继承:
    • 标准DIN连接到
      管道[1]
    • 标准输出已连接到
      文件
  • 在这里,管道优先,即在执行管道元素的任何其他操作之前,由于管道而执行stdin/stdout的重定向。因此,这两个
    echo
    都继承了重定向到
    cat
    stdout



    所有这些实际上都是在进程替换之后处理文件重定向的设计结果。如果在此之前处理了
    >文件
    重定向(就像管道重定向一样),那么
    >文件
    也会被替换的进程继承。

    另一方面,
    echo-e
    被POSIX指定为在输出时发出
    -e
    { echo -e '1\n2' | tee >(head -n1); } > redirect
    
    echo -e '1\n2' | tee >(head -n1) | cat > pipe
    
    $ echo x >(echo y) >file
    y
    $ cat file
    x /dev/fd/63
    
    $ echo x >(echo y) | cat >file
    $ cat file
    x /dev/fd/63
    y