在shell脚本中使用mkfifo

5
我尝试使用FIFO做一些简单的事情:读取行,但不是一次性读取,结果意外地“没有起作用”。
这是可以的:
$ f=$(mktemp -u)
$ mkfifo $f
$ { seq 5 > $f; } &
[1] 2486
$ while read line; do echo $line; done < $f
1
2
3
4
5
[1]+  Done                    { seq 10 > $f; }

但是如果我逐行阅读,第一次读取成功,而第二次读取则会挂起。

$ { seq 5 > $f; } &
[1] 2527
$ read line < $f; echo $line
1
[1]+  Done                    { seq 5 > $f; }
$ read line < $f; echo $line
[hangs here...]

有人能解释一下吗?为什么我不能逐行阅读所有5行?剩下的数据发生了什么事?


我发现如果我创建一个文件描述符来重定向FIFO,就可以逐行阅读:

$ { seq 5 > $f; } &
[1] 2732
$ exec 3<$f
[1]+  Done                    { seq 5 > $f; }
$ read -u 3 line && echo $line || echo no more data
1
$ read -u 3 line && echo $line || echo no more data
2
$ read -u 3 line && echo $line || echo no more data
3
$ read -u 3 line && echo $line || echo no more data
4
$ read -u 3 line && echo $line || echo no more data
5
$ read -u 3 line && echo $line || echo no more data
no more data
$ exec 3<&-

我仍然不理解中间的情景。有人能解释一下吗?

版本信息:

$ bash --version
GNU bash, version 4.2.25(1)-release (i686-pc-linux-gnu)
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ mkfifo --version
mkfifo (GNU coreutils) 8.13
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written by David MacKenzie.

这可能与版本、bash vs ksh或其他因素有关。你能添加这些信息吗?(无论如何,我都不知道答案;-( )。这是本季度最有趣的问题! - shellter
在OSX Mavericks和Ubuntu VirtualBox上的行为完全相同。 - Mark Setchell
您的第三种情况无法重现。 - devnull
@devnull,真的吗?你用的是什么shell? - glenn jackman
@glennjackman GNU bash,版本4.2.25(1)-release(x86_64-pc-linux-gnu) - devnull
2个回答

6
我猜发生的情况是这样的: $ read line < $f 打开FIFO进行读取,读取一行后关闭了FIFO。一旦读取程序在其端关闭了FIFO,写入程序(seq 5 > $f)也会关闭。下次打开FIFO时没有人向其中写入,所以read命令就会被阻塞。
使用while命令可以让FIFO保持开放状态,直到while命令结束,从而允许写入程序向FIFO发送更多行。
您可以使用lsof -p $$来验证每个点上打开(或未打开)的文件。

1
谢谢。我看GNU信息页面上说:“一个进程打开FIFO文件进行写入,另一个进程进行读取”——这意味着你所说的。 - glenn jackman

2

这里似乎有一些关于 FIFO 的混淆。

当你说:

read line < $f

它会打开FIFO,寻找、读取并在关闭后完成。无论你读一行还是整个数据都没有关系。

所以当你尝试再次读取时,read会一直等待。

当它在等待时,尝试说:

echo foo > $f

你会注意到read操作成功。

如果你同时监控两种情况下的系统调用,你会发现第二个read操作正在等待FIFO中的读取。

不确定你在这里想要完成什么,但你的观察结果基本符合预期。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接