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

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

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

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

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

对于其他值也是如此:

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

$ echo $BASHPID >(cat)
3252 /dev/fd/63

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

答案1

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

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

$ sort -o >(cat -n >/tmp/out) ~/.profile

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

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

稍微修改一下你的示例代码:

$ echo 101 > >(cat)

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


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

答案2

为了完整性

cmd1 >(cmd2)

大部分是相同的

cmd1 | cmd2

yash外壳中,并且仅在外壳中。

在那个外壳中,>(cmd)是进程重定向>(cmd)与of ksh//相对,bash后者zsh是 process代换

它并不严格等效,因为 in cmd1 >(cmd2),yash不等待cmd2,所以您可能会发现:

$ yash -c 'echo A >(cat); echo B'
B
A
$ yash -c 'echo A | cat; echo B'
A
B

相比之下,过程代换扩展为文件路径(通常是命名管道或预先创建的管道的 fd),当打开进行写入时,将允许将输出发送/dev/fd/<x>到.<x>cmd

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

ksh -c 'cmd1 > >(cmd2)'

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

ksh -c 'diff <(echo a) <(echo b)'

它将在bash和中工作zsh

然而,bash就像 for 的进程重定向一样yash,shell 不会等待命令 ( cmd2)。所以:

$ bash -c 'echo A > >(cat); echo B'
B
A

ksh进程替换可以yash通过以下方式进行模拟:

cmd1 /dev/fd/5 5>(cmd2)   

喜欢:

diff /dev/fd/3 3<(echo a) /dev/fd/4 4<(echo b)

答案3

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

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

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

echo foo > >(cat -n)

答案4

$ echo 1 >(cat > /dev/null)
1 /dev/fd/63
$ echo echo >(cat /dev/null)
echo /dev/fd/63

# We can trace how the commands are executed
# so long as we avoid using shell builtin commands,
# and run the equivalent external program instead, i.e. /usr/bin/echo

$ strace -f -e execve bash -c '/usr/bin/echo >(cat /dev/null)'
execve("/usr/bin/bash", ["bash", "-c", "/usr/bin/echo >(cat /dev/null)"], [/* 56 vars */]) = 0
strace: Process 4213 attached
[pid  4212] execve("/usr/bin/echo", ["/usr/bin/echo", "/dev/fd/63"], [/* 56 vars */]) = 0
strace: Process 4214 attached
[pid  4214] execve("/usr/bin/cat", ["cat", "/dev/null"], [/* 56 vars */]/dev/fd/63
) = 0
[pid  4212] +++ exited with 0 +++
[pid  4214] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=4214, si_uid=1001, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++

# Apparently, the order of evaluation is arranged so this works nicely:

$ echo 1 > >(cat > /dev/null)
$

相关内容