質問

I was trying to do something simple with a FIFO: read the lines, but not all at once, and it unexpectedly "did not work".

This is OK:

$ 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; }

But if I try reading lines one-by-one, the first read succeeds and the 2nd read hangs.

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

Can someone explain this? Why can't I read all 5 lines one-by-one? What happened to the rest of the data?


I discovered I can read line-by-line if I create a file descriptor to redirect the 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<&-

I still don't understand the middle scenario. Can anyone explain?


Version info:

$ 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.
役に立ちましたか?

解決

I guess what happens is this:

$ read line < $f opens the FIFO for reading, reads one line, then closes the FIFO. Once the reader closes the FIFO on its side, the writer (seq 5 > $f) also closes. When you open the FIFO next time nobody writes to it at that point, so the read blocks.

With the while the FIFO is open for reading until the while command finishes, allowing the writer to send more lines to the FIFO.

You can use lsof -p $$ to verify what files are (not) open at each point.

他のヒント

It seems that there is some confusion about FIFO here.

When you say:

read line < $f

it'd open the FIFO, seek, read, and be done with it after closing. It doesn't matter whether you read a line or the entire data.

So when you attempt to read again, the read keeps waiting.

While it's waiting, try saying:

echo foo > $f

and you'd notice that the read is successful.

If you were monitoring the system calls being made in both the cases, you'd observe that the second read is waiting to read from the FIFO.

Not sure what you're trying to accomplish here but your observation is pretty much along expected lines.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top