在尝试输出重定向和进程替换时,我偶然发现了以下命令及其结果输出:
me@elem:~$ echo foo >>(cat);回声栏 酒吧 我@elem:~$ foo
(是的,末尾的空换行符是故意的。)
所以 bash echo 的 bar,打印我常用的提示符,echo 的 foo,echo 的换行符,并将光标留在那里。如果我再次按回车键,它将在新行上打印我的提示符并将光标保留在其后面(正如有人在空命令行上按回车键时所预期的那样)。
我期望它将 foo 写入文件描述符,cat 读取它并回显 foo,第二个回显 echo 的 bar,然后返回命令提示符。但事实显然并非如此。
有人可以解释一下发生了什么事吗?
答案1
您可能会在提示foo
之前bar
、之后bar
甚至之后看到显示内容,具体取决于时间。添加一点延迟以获得一致的计时:
$ echo foo > >(sleep 1; cat); echo bar; sleep 2
bar
foo
$
bar
立即出现,然后foo
一秒钟后出现,然后再过一秒后出现下一个提示。
发生的情况是 bash 在后台执行进程替换。
- 主 bash 进程启动一个子 shell 来执行
sleep 1; cat
,并为其设置一个管道。 - 主 bash 进程执行
echo foo
.由于这不会填满管道的缓冲区,因此该echo
命令将终止而不会阻塞。 - 主 bash 进程执行
echo bar
. - 主 bash 进程启动命令
sleep 2
。 - 同时,子 shell 正在启动命令
sleep 1
。 - 大约1秒后,
sleep 1
返回到子进程中。子进程继续执行cat
。 cat
将其输入复制到其输出(显示在屏幕上)并返回。- 子 shell 已完成其工作,退出。
- 又过一秒,
sleep 2
返回。主 shell 进程完成执行,您将看到下一个提示。
答案2
您不需要替换进程即可获得该效果。尝试这个:
( echo foo | cat & )
您将得到大致相同的结果(没有bar
;我将把它作为练习)并且出于相同的原因。在这两种情况下,cat
都是作为后台任务启动的。在我这里,这是明确的。在进程替换的情况下,它也是明确的——它是一个附加到名称文件描述符的单独进程——但它可能不那么明显。
bash
在打印下一个提示之前,子进程不一定终止。由于它的输出是缓冲的,因此在终止之前不会输出任何内容。此时,bash 刚刚$
在 stderr 上打印,现在后台进程在打印foo
和换行后退出。