我期望cat <(cat)
并cat | cat
做同样的事情:将行从标准输入复制到标准输出。我的理解是,两者都会cat
在 subshell 中执行 a ,将 subshel cat
l 的 stdout 重定向到临时命名管道,然后cat
在当前 shell 中执行另一个,并将其 stdin 重定向到管道。
相反,cat <(cat)
让我在终端上输入,但没有任何输入行被复制并且^D
无法发出信号EOF
;cat | cat
但按预期工作。
作为进一步的实验,我检查了是否cat =(cat)
有与 类似的困难cat <(cat)
,但它按我的预期工作:所有 stdin 到 a^D
都一次性复制到 stdout。
谁能帮我理解 zsh 在幕后做了什么?
答案1
a | b
STDOUT
只需使用a
即可连接STDIN
b
dup/dup2
。两个命令并行执行。a =(b)
将参数替换为a
临时文件名。b
将在之前执行,a
因为需要先创建临时文件才能将其传递给a
a <(b)
将参数替换为a
命名管道。a
并b
并行运行。现在事情变得有点复杂:•
b
处于后台且无法从终端读取。您可以通过使用strace -p $PID
附加到第二个 cat 进程来查看该进程来自行测试。•
a
同时尝试从命名管道读取,但无法读取任何内容,因为b
无法读取。• 这意味着您基本上遇到了死锁,
a
尝试读取b
但b
无法读取STDIN
且无法写入a
有关后台进程和终端的更多信息来自男人狂欢:
为了便于实现作业控制的用户界面,操作系统维护了一个概念:当前终端进程组ID。该进程组的成员(进程组 ID 等于当前终端进程组 ID 的进程)接收键盘生成的信号,例如 信号情报。据说这些过程是在前景。背景进程是那些进程组ID与终端的进程组ID不同的进程;这些进程不受键盘生成的信号的影响。仅允许前台进程读取或写入终端(如果用户使用 stty tostop 指定)。尝试从终端读取(当 stty tostop 有效时写入)的后台进程将被发送西格丁 (SIGTTOU)由内核的终端驱动程序发出的信号,除非被捕获,否则会挂起进程。