为什么“echo 123 >(cat)”的输出中有“/dev/fd/63”?

sha*_*ant 7 bash pipe io-redirection file-descriptors process-substitution

$ echo 123 | cat 
123
Run Code Online (Sandbox Code Playgroud)

正在做我所期望的,两个命令都在同一个 shell 中运行。

但是,当我将它们与>( ... )表达式连接起来时,该表达式将 shell 中的一个命令的输出连接到子 shell 中的第二个命令,我得到了这个:

$ echo 123 >(cat)
123 /dev/fd/63
Run Code Online (Sandbox Code Playgroud)

对于其他值也是如此:

$ echo 101 >(cat)
101 /dev/fd/63

$ echo $BASHPID >(cat)
3252 /dev/fd/63
Run Code Online (Sandbox Code Playgroud)

我认为command1 >(command2)与 相同command1 | command2,但是在 中command1 >(command2),每个命令都在不同的 shell 中,因此它们应该具有相同的输出。我哪里错了?

Kus*_*nda 11

进程替换>(thing)将被文件名替换。此文件名对应于连接到thing内部替换的标准输入的文件。

以下将是其使用的更好示例:

$ sort -o >(cat -n >/tmp/out) ~/.profile
Run Code Online (Sandbox Code Playgroud)

这将对文件进行排序~/.profile并将输出发送到cat -n枚举行并将结果存储在/tmp/out.

所以,回答你的问题:你得到那个输出是因为echo得到了两个参数123/dev/fd/63/dev/fd/63cat在进程替换中连接到进程标准输入的文件。

稍微修改您的示例代码:

$ echo 101 > >(cat)
Run Code Online (Sandbox Code Playgroud)

这将仅101在标准输出上产生( 的输出echo将被重定向到作为 输入的文件cat,并将cat在标准输出上产生该文件的内容)。


另请注意,在cmd1 | cmd2管道中,cmd2可能根本不在同一 shell 中运行cmd1(取决于您使用的 shell 实现)。 ksh93以您描述的方式工作(相同的外壳),同时为其bash创建一个子外壳cmd2(除非lastpipe设置了它的外壳选项并且作业控制未处于活动状态)。


Sté*_*las 6

为了完整性

cmd1 >(cmd2)
Run Code Online (Sandbox Code Playgroud)

大部分是一样的

cmd1 | cmd2
Run Code Online (Sandbox Code Playgroud)

yash壳中,只有那个壳。

在该外壳,>(cmd)是处理重定向而不是在>(cmd)ksh/ bash/zsh其是过程替代

它不是严格等价的,因为 in cmd1 >(cmd2)yash不等待cmd2,所以你可能会发现:

$ yash -c 'echo A >(cat); echo B'
B
A
$ yash -c 'echo A | cat; echo B'
A
B
Run Code Online (Sandbox Code Playgroud)

相比之下,处理置换扩展到一个文件路径(通常是命名管道或/dev/fd/<x>其中<x>是一个FD到已预先创建的配管),其在打开时用于写入将允许将输出发送到cmd

虽然进程替换是由 引入的ksh,但在该 shell 中,您不能将它们作为参数传递给重定向。

ksh -c 'cmd1 > >(cmd2)'
Run Code Online (Sandbox Code Playgroud)

模拟yash进程重定向将不起作用。在那里,您打算将替换产生的文件名作为参数传递给如下命令:

ksh -c 'diff <(echo a) <(echo b)'
Run Code Online (Sandbox Code Playgroud)

它将在bash和 中工作zsh

但是,bash就像 foryash的进程重定向一样,shell 不会等待命令 ( cmd2)。所以:

$ bash -c 'echo A > >(cat); echo B'
B
A
Run Code Online (Sandbox Code Playgroud)

ksh可以使用以下方法模拟进程替换yash

cmd1 /dev/fd/5 5>(cmd2)   
Run Code Online (Sandbox Code Playgroud)

喜欢:

diff /dev/fd/3 3<(echo a) /dev/fd/4 4<(echo b)
Run Code Online (Sandbox Code Playgroud)


ilk*_*chu 5

因为这就是进程替换所做的,它使替换中的命令显示为文件名。在内部,它通过管道连接命令,提供/dev/fd/NN主命令的路径,因此它可以将已经打开的文件描述符打开到管道。

它与管道不同。管道连接stdoutstdin不涉及任何看起来像文件名的东西。进程替换更加灵活,因为您可以在一个命令行中进行多个这样的替换,但它需要主命令按名称打开文件(例如,cat而不是echo)。

您可以通过执行以下操作来模拟带有进程替换的管道:

echo foo > >(cat -n)
Run Code Online (Sandbox Code Playgroud)