Bash 重定向:命名管道和 EOF

Posted

技术标签:

【中文标题】Bash 重定向:命名管道和 EOF【英文标题】:Bash redirection: named pipes and EOF 【发布时间】:2015-10-15 22:55:37 【问题描述】:

取以下代码:

rm -f pipe
mkfifo pipe

foo () 
    echo 1
    sleep 1
    echo 2


#1
exec 3< <(foo &)
cat <&3 # works

#2
foo >pipe &
cat <pipe # works

#3
exec 3<>pipe
foo >&3 &
cat <&3 # hangs

#4 -- update: this is the correct approach for what I want to do
foo >pipe &
exec 3<pipe
rm pipe
cat <&3 # works

为什么方法 #3 会挂起,而其他方法则不会?有没有办法让方法 #3 不挂起?

理由:我希望使用准无名管道来连接几个异步运行的子进程,为此我需要在文件描述符指向它后删除管道:

mkfifo pipe
exec fd<>pipe
rm pipe
# use &$fd only

【问题讨论】:

【参考方案1】:

方法 3 中的问题是 FIFO pipe 然后有 2 个写入器:bash 脚本(因为您已使用 exec 3&lt;&gt; 读取/写入打开它)和运行 foo 的子 shell。当所有作者都关闭文件描述符时,您将读取 EOF。一个编写器(运行foo 的子shell)将很快退出(大约1 秒后)并因此关闭文件描述符。然而,另一个编写器(主 shell)仅在文件描述符退出时关闭文件描述符,因为在任何地方都没有关闭文件描述符 3。但它无法退出,因为它等待cat 先退出。这是一个僵局:

cat 正在等待 EOF EOF 仅在主 shell 关闭 fd(或退出)时出现 主 shell 正在等待 cat 终止

因此你永远不会退出。

案例 2 有效,因为管道只有一个写入器(运行 foo 的子 shell),它退出非常快,因此将读取 EOF。在情况 1 中,也只有一个写入器,因为您以只读方式打开 fd 3 (exec 3&lt;)。

编辑:删除关于案例 4 不正确的废话(参见 cmets)。这是正确的,因为在阅读器连接之前编写器无法退出,因为在阅读器尚未打开时打开文件时它也会被阻止。 不幸的是,新添加的案例 4 不正确。它很活泼,只有当fooexec 3&lt;pipe 运行之前没有终止(或关闭管道)时才有效。

同时查看fifo(7) 手册页:

内核为每个由至少一个进程打开的 FIFO 特殊文件维护一个管道对象。 FIFO 必须在两端(读取和写入)都打开,才能传递数据。通常,打开FIFO会阻塞,直到另一端也打开。

【讨论】:

成功了。 exec 3&lt;&gt; 使主 shell 成为编写器,这会导致 EOF 问题。 @Irfy 我补充说。在情况 1 中,文件描述符 3 在主 shell 中以只读方式打开。因此,管道永远不会有超过 1 个作者。 在此帮助下,我重写了删除管道的代码,使其按预期工作,谢谢。 @Irfy 不幸的是,#4 也不是真的正确。这实际上是一个竞争条件:如果 fooexec 3&lt;pipe 运行之前退出,exec 3&lt;pipe 将挂起,直到另一个编写器打开 pipe @Irfy 是的,你说得对!编写器 (foo &gt;pipe ) 仅在读取器 (exec 3&lt;pipe) 连接时开始运行。所以你们都很好。很抱歉造成混乱。

以上是关于Bash 重定向:命名管道和 EOF的主要内容,如果未能解决你的问题,请参考以下文章

将子进程的 stdout 和 stderr 重定向到两个命名管道(然后从它们读回)

键盘输入错误地重定向到命名管道读取

将命名管道输入重定向到文件

无法将多个 shell 语句重定向到命名管道

/bin/sh:如何从 &3 重定向到命名管道?

第4天文件管理管道用户及组管理用户及权限管理