Bash 重定向:命名管道和 EOF

Bash redirection: named pipes and EOF

取以下代码:

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

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

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

所以你永远不会退出。

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

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

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

The kernel maintains exactly one pipe object for each FIFO special file that is opened by at least one process. The FIFO must be opened on both ends (reading and writing) before data can be passed. Normally, opening the FIFO blocks until the other end is opened also.