How to Fix Hanging Popen3 in Ruby

How to fix hanging popen3 in Ruby?

stdout.each_line is waiting for further output from cat because cat's output stream is still open. It's still open because cat is still waiting for input from the user because its input stream hasn't been closed yet (you'll notice that when you open cat in a terminal and type in foobar, it will still be running and waiting for input until you press ^d to close the stream).

So to fix this, simply call stdin.close before you print the output.

Ruby Open3.popen3 simulate user input

Got my solution here:
How to fix hanging popen3 in Ruby?


Open3.popen3(@command) do |stdin, stdout, stderr|
stdin.puts "y\r\n"

stdout.each_line { |line| puts line }
stdin.close
end

Ruby—Open3.popen3 / how to print the output


  • Since you are only writing to stdout, you can simply use Open3#popen2e which consolidates stdout and stderr into a single stream.
  • To write newline terminated strings to a stream, you can use puts as you would with $stdout in a simple hello world program.
  • You must use waith_thread.join or wait_thread.value to wait until the child process terminates.
  • In any case, you will have to start a separate thread for reading from the stream, if you want to see the results immediately.

Example:

require 'open3'

cmd = 'sh'

Open3.popen2e(cmd) do |stdin, stdout_stderr, wait_thread|
Thread.new do
stdout_stderr.each {|l| puts l }
end

stdin.puts 'ls'
stdin.close

wait_thread.value
end

Your code, fixed:

require 'open3'

mysqldump = # ...

mysqlimp = "mysql -u #{mysqllocal['user']} "
mysqlimp << "-h #{mysqllocal['host']} "
mysqlimp << "-p#{mysqllocal['pass']} "
mysqlimp << "#{mysqllocal['db']}"

Open3.popen2e(mysqlimp) do |stdin, stdout_stderr, wait_thread|
Thread.new do
stdout_stderr.each {|l| puts l }
end

stdin.puts "DROP DATABASE IF EXISTS #{mysqllocal['db']};"
stdin.puts "CREATE DATABASE #{mysqllocal['db']};"
stdin.puts "USE #{mysqllocal['db']};"
stdin.close

wait_thread.value
end

basic popen3 syntax in ruby

In the first form you should explicitly close stdin, stdout and stderr.

ruby - IO.popen not working lame stdin and stdout encoding

Did you try the pipe | character?

Tested this on windows with ruby installer

require 'open3'

command = 'dir /B | sort /R' # a windows example command
Open3.popen3(command) {|stdin, stdout, stderr, wait_thr|
pid = wait_thr.pid
puts stdout.read #<a list of files in cwd in reverse order>
}

Other ways: Ruby pipes: How do I tie the output of two subprocesses together?

EDIT:
using IO::pipe

require 'open3'

command1 = 'dir /B'
command2 = 'sort /R'

reader,writer = IO.pipe
Open3.popen3(command1) {|stdin, stdout, stderr, wait_thr|
writer.write stdout.read
}
writer.close

stdout, stderr, status = Open3.capture3(command2, :stdin_data => reader.read)
reader.close

puts "status: #{status}" #pid and exit code
puts "stderr: #{stderr}" #use this to debug command2 errors
puts stdout

Embedding the two also appears to work, yet, as the blog you referred to said, one must wait for the first command to finish (not real-time -- test with a ping command)

stdout2 = ''
Open3.popen3(command1) {|stdin, stdout, stderr, wait_thr|
stdout2, stderr2, status2 = Open3.capture3(command2, :stdin_data => stdout.read)
}
puts stdout2


Related Topics



Leave a reply



Submit