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 consolidatesstdout
andstderr
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
orwait_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
Extracting the Last N Characters from a Ruby String
Passing Headers and Query Params in Httparty
How to Unfreeze an Object in Ruby
How to Spawn a Child Process in Ruby
Handling Namespace Models (Classes) in Namespace
How to Share Variables Across My .Rb Files
Getting the Highest Value of a Column in Mongodb
Case-Insensitive Array#Include
Scope of Constants in Ruby Modules
Class Method VS Constant in Ruby/Rails
Rspec: How to Stub an Instance Method Called by Constructor
Parse CSV File with Header Fields as Attributes for Each Row
How to Get Elapsed Time in Milliseconds in Ruby
Is There a Literal Notation for an Array of Symbols