Reputation: 12412
I'm trying to find some documentation on how STDIN
is handled in Ruby.
I've experimented with this simple script:
# test.rb
loop do
puts "stdin: #{$stdin.gets}"
sleep 2
end
That I've run from bash (on OS X) with:
$ ruby test.rb
As I expected, the call to $stdin.gets
is blocking, and the loop waits for the next input. The 2 second sleep time even allows me to enter more lines in one go, and the loop correctly prints them in order, then stops again when STDIN is cleared:
$ ruby test.rb
a
stdin: a
b
stdin: b
c
d
e
stdin: c
stdin: d
stdin: e
So far, all good. I was expecting this.
Then, I made a test with a pipe:
$ mkfifo my_pipe
$ ruby test.rb < my_pipe
And, in another shell:
$ echo "Hello" > my_pipe
This time, it behaved a bit differently.
At first it did wait, blocking the loop. But then, after the first input was passed through the pipe, it keept looping and printing empty strings:
$ ruby test.rb
stdin: Hello
stdin:
stdin:
stdin: Other input
stdin:
So my question is: why the difference? Does it treat the pipe like an empty file? Where is this documented? The docs don't say anything about the blocking behaviour, but they do say that:
Returns nil if called at end of file.
It's a start.
Upvotes: 0
Views: 545
Reputation: 809
So the short answer is yes, you are getting an EOF from the pipe. Because the way echo works is that it's going to open the pipe for writing, write to it, then close (i.e. send EOF). Then a new call to echo will open it back up, read to it, and then close.
If you had instead used a program that printed lines of a file after a 3 second sleep, you would see that your application would perform blocking waits until that one exits (at which point the never-ending EOFs would return).
# slow_write.rb
ARGF.each do |line|
puts line
STDOUT.flush
sleep 3
end
I should note that this behavior is not specific to Ruby. The C stdlio library has the exact same behavior and since most languages use C primitives as their basis they have the same behavior as well.
Upvotes: 1