在后台进程中使用命名管道时的不同行为

Varying behavior when using named pipe with background process

我很难理解命名管道。我有以下脚本:

#!/bin/bash

function a_or_b {
   echo 'func begins'

   while true; do
      echo 'loop begins'

      read -s -n 1; echo $?
      case $REPLY in
         'a') return 0 ;;
         'b') return 1 ;;
      esac
   done

   echo 'func ends'
}

mkfifo pipe
a_or_b <pipe &

现在我期待这个脚本做的是:

  1. 输入a_or_b然后打印func begin
  2. 进入循环并打印loop begins
  3. stdin 读取一个 EOF,因此从 pipe(因为我没有写任何东西到 pipe),因此将 1 打印为 $?
  4. 两个 case 从句都不匹配,因此返回步骤 2

当 运行 这个脚本虽然我没有输出,我的终端只是打印它的下一个提示。


如果我在调用 a_or_b:

之前将 echo 重定向到 pipe
...

mkfifo pipe
echo 'x' > pipe
a_or_b <pipe &

...脚本不会停止 运行,我可以继续在终端中输入字符(包括 a 和 'b'),但没有任何效果。所以我必须使用 ^C.

结束脚本

如果我将 echo 重定向到 pipe after 调用 a_or_b:

...

mkfifo pipe
a_or_b <pipe &
echo 'x' > pipe

...我得到以下输出:

func begins
loop begins
0
loop begins
0
loop begins
1
loop begins
1
loop begins
1

这基本上是我预期的行为,从 回显 任何东西到 pipe。函数开始,进入循环,从echo中读取x\n字符(对应输出中的两个0),然后一直循环下去而无法读取任何字符。如果我 echo ab 进入管道,函数结束。


是什么导致所有这些不同的行为?

a_or_b <pipe &

在启动命令之前处理重定向。 shell 块试图打开 pipe 进行阅读。来自 mkfifo(3) man page:

Opening a FIFO for reading normally blocks until some other process opens the same FIFO for writing, and vice versa.

在另一个进程打开 FIFO 进行写入之前,shell 无法继续。只有这样它才会完成重定向设置并实际调用 a_or_b.


echo 'x' > pipe
a_or_b <pipe &

不幸的是,这有相同但相反的问题。在另一个进程打开 FIFO 进行读取之前,shell 无法继续通过 > pipe 重定向。它永远不会到达 echo 或从管道读取的第二行。


a_or_b <pipe &
echo 'x' > pipe

希望您现在可以明白为什么这个版本有效。后台 shell 尝试从管道和块中读取。前台shell,一个单独的进程,写入它。啊哈!两个进程打开了管道,一个用于读取,一个用于写入。他们现在都可以继续了。鸟语花香,皆大欢喜。