2

I am using the open4 gem and having problems reading from the spawned processes stdout. I have a ruby program, test1.rb:

print 'hi.' # 3 characters
$stdin.read(1) # block

And another ruby program in the same directory, test2.rb:

require 'open4'

pid, stdin, stdout, stderr = Open4.popen4 'ruby test1.rb'
p stdout.read(2) # 2 characters

When I run the second program:

$ ruby test2.rb

It just sits there forever without printing anything. Why does this happen, and what can I do to stop it?

Adrian
  • 14,018
  • 7
  • 41
  • 67

3 Answers3

2

By default, everything that you printto stdout or to another file is written into a buffer of Ruby (or the standard C library, which is underneath Ruby). The content of the buffer is forwarded to the OS if one of the following events occurs:

  • The buffer gets full.
  • You close stdout.
  • You have printed a newline sequence (`\n')
  • You call flush explicitly.

For other files, a flush is done on other occasions, too, like ftell.

If you put stdout in unbuffered mode ($stdout.sync = true), the buffer will not be used.

stderr is unbuffered by default. The reason for doing buffering is efficiency: Aggregating output data in a buffer can save many system call (calls to operating system). System calls are very expensive: They take many hundreds or even thousands of CPU cycles. Avoiding them with a little bit of code and some buffers in user space results in a good speedup.

A good reading on buffering: Why does printf not flush after the call unless a newline is in the format string?

Community
  • 1
  • 1
hagello
  • 2,173
  • 1
  • 24
  • 34
2

I needed to change test1.rb to this. I don't know why.

print 'hi.' # 3 characters
$stdout.flush
$stdin.read(1) # block
Adrian
  • 14,018
  • 7
  • 41
  • 67
1

I'm not an expert in process.

From my first sight of API document, the sequence of using open4 is like this: first send text to stdin, then close stdin and lastly read text from stdout.

So. You can the test2.rb like this

require 'open4'

pid, stdin, stdout, stderr = Open4.popen4 'ruby test1.rb'
stdin.puts "something" # This line is important
stdin.close # It might be optional, open4 might close itself.
p stdout.read(2) # 2 characters
OmniBus
  • 834
  • 1
  • 6
  • 24