Reputation: 6683
Can someone explain to me how pipe.gets
works in this instance? is IO object (pipe in this example), buffering the output? How is it that I can take my time to read the output from stdout using "gets"? I even put a sleep in there before reading through gets
to make sure I'm not crazy.
def run_script(commands)
raw_output = nil
IO.popen("./db", "r+") do |pipe|
commands.each do |command|
pipe.puts command
end
pipe.close_write
# Read entire output
raw_output = pipe.gets(nil)
end
raw_output.split("\n")
end
Upvotes: 2
Views: 476
Reputation: 21130
The pipe.sync
options defaults to false
for popen
. Meaning that the commands are buffered by Ruby until you call pipe.close_write
, pipe.close
, exit the program or the buffer is full. The buffer is then flushed, providing all buffered data written to the other program.
For more info check out the IO#sync
documentation and/or What does “file.sync = true” do?
Based on your comment I'm not sure I understand the question. The only reason I can think of why pipe.gets(nil)
would return nil
is if the other program has no output.
The other option is that pipe.gets(nil)
will block. This could happen if pipe
is never flushed, meaning the other program is still waiting for the input. Because the other program now blocks and hasn't closed their standard output pipe.gets(nil)
will also block. pipe.gets(nil)
will read everything from the standard input. How does it know everything is read? It knows because a closed connection means there can't be send any data, thus it waits for a closed connection.
Ruby stdout -------> stdin ./db
stdin <-------- stdout
(blocked because empty)
pipe.puts("a") "a\n" -------> empty <read from stdin>
empty <-------- empty
(still blocked)
pipe.puts("b") "a\nb\n" -----> empty <read from stdin>
empty <-------- empty
(flushing stdout) (continues consuming "a\nb\n")
pipe.close_write empty ----/---> "a\n\b" <read from stdin>
empty <-------- empty
(blocks because empty)
pipe.gets(nil) empty ----/---> empty <send data to stdout>
empty <-------- "data\n"
(continues consuming "data\n") (flushing stdout)
pipe.gets(nil) empty ----/---> empty <close stdout>
"data\n" <--/-- empty
I hope the above helps demonstrate the process of your current code.
Upvotes: 3