两个窗口,同一用户,带有 bash 提示符。在 window-1 中输入:
$ mkfifo f; exec <f
因此 bash 现在尝试从文件描述符 0 读取,该描述符映射到命名管道f
。在窗口 2 中输入:
$ echo ls > f
现在 window-1 打印一个 ls 然后 shell 就死掉了。为什么?
下一个实验:使用 再次打开 window-1 exec <f
。在窗口 2 中输入:
$ exec 3>f
$ echo ls >&3
在上面的第一行之后,window-1 被唤醒并打印一条提示。为什么?在上面的第二行之后,window-1 打印ls
输出并且 shell 保持活动状态。为什么?事实上,现在在window-2中,echo ls > f
并没有关闭window-1 shell。
答案一定与存在window-2 中的文件描述符 3 引用命名管道?!
答案1
这与关闭文件描述符的。
在第一个示例中,echo
写入其标准输出流,外壳程序打开该流以将其与 进行连接f
,并且当它终止时,其描述符将被关闭(由外壳程序)。在接收端,从其标准输入流(连接到f
)读取输入的 shell会读取ls
、运行ls
,然后由于其标准输入上的文件结束条件而终止。
发生文件结束条件是因为命名管道的所有编写者(本示例中只有一个)都已关闭其管道末尾。
在第二个示例中,exec 3>f
打开文件描述符 3 以写入f
,然后echo
写入ls
它。现在是 shell 打开了文件描述符,而不是命令echo
。描述符将保持打开状态,直到您执行此操作exec 3>&-
。在接收端,从其标准输入流(连接到f
)读取输入的 shell读取ls
、运行ls
,然后等待更多输入(因为流仍然打开)。
流保持打开状态,因为它的所有写入者(shell、viaexec 3>f
和echo
)都已不是关闭了管道的末端(exec 3>f
仍然有效)。
我在上面写的就echo
好像它是一个外部命令一样。它很可能内置于外壳中。尽管如此,效果是一样的。
答案2
答案3
阅读了@Kusalananda 和@ikkachu 的两个答案后,我想我明白了。在 window-1 中,shell 正在等待某些东西打开管道的写入端然后将其关闭。一旦打开写入端,window-1 中的 shell 就会打印一条提示。一旦写端关闭,shell 就会收到 EOF 并终止。
在 window-2 方面,我们有我的问题中描述的两种情况:在第一种情况下echo ls > f
,没有文件描述符 3,所以我们echo
产生了,它的stdin
和stdout
看起来像这样:
0 --> tty
1 --> f
然后echo
终止并且 shell 关闭两个描述符。由于文件描述符 1 已关闭并引用f
,因此 的写入结束f
已关闭,这会导致 window-1 出现 EOF。
在第二种情况下,我们exec 3>f
在 shell 中运行,使 shell 采用此环境:
bash:
0 --> tty
1 --> tty
2 --> tty
3 --> f
现在我们运行echo ls >& 3
,shell 分配文件描述符如下echo
:
echo:
0 --> tty
1 --> f # because 3 points to f
2 --> tty
然后 shell 关闭上面的三个描述符,包括f
,但f
仍然有 shell 本身对它的引用。这是重要的区别。关闭描述符 3exec 3>&-
将关闭最后一个打开的引用并导致 EOF 到 window-1,如 @Kusalananda 指出的。