$ 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
主命令的路径,因此它可以打开管道中已经打开的文件描述符。
它与管道不一样。管道连接stdout
不stdin
涉及任何看起来像文件名的内容。进程替换更加灵活,因为您可以在一个命令行中进行多个此类替换,但它需要主命令按名称打开文件(例如,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)
$