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.
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
Runy Open3.popen3 Entering input into the subprocess from the command-line
After a lot of reading about STDIN as well as some good old trial and error, I discovered an implementation not to dissimilar to Charles Finkel's answer but with some subtle differences.
require "open3"
module Cmd
def run(cmd, &block)
Open3.popen3(cmd) do |stdin, stdout, stderr, thread|
# We only need to check if the block is provided once
# rather than every cycle of the loop as we were doing
# in the original question.
if block_given?
Thread.new do
until (line = stdout.gets).nil? do
yield line, nil, thread
end
end
Thread.new do
until (line = stderr.gets).nil? do
yield nil, line, thread
end
end
end
# $stdin.gets reads from the console
#
# stdin.puts writes to child process
#
# while thread.alive? means that we keep on
# reading input until the child process ends
Thread.new do
stdin.puts $stdin.gets while thread.alive?
end
thread.join
end
end
end
include Cmd
Calling the method like so: run './test_script.sh' do | stdout, stderr, thread|
puts "#{thread.pid} stdout: #{stdout}" if stdout
puts "#{thread.pid} stderr: #{stderr}" if stderr
end
Where test_script.sh
is as follows:echo "Message to STDOUT"
>&2 echo "Message to STDERR"
echo "enter username: "
read username
echo "enter a greeting"
read greeting
echo "$greeting $username"
exit 0
Produces the following successful output:25380 stdout: Message to STDOUT
25380 stdout: enter username:
25380 stderr: Message to STDERR
> Wayne
25380 stdout: enter a greeting
> Hello
25380 stdout: Hello Wayne
Note: You will notice the stdout and stderr don't appear in order, this is a limitation I'm yet to solve.If you're interested in knowing more about stdin it's worth reading the following answer to the question - What is the difference between STDIN and $stdin in Ruby?
Open3.popen3 returns wrong error Errno::ENOENT on Windows
You are having trouble because "Program Files" is a folder with a space in it. Whenever that happens, you need to double quote it, just as you would on a cmd.exe prompt. And when you're double-quoting, you must remember that your backslash character "\" is an escape character, so you have to double-backslash to get the proper folder separators for Windows. I'm going to use code which actually returns something in my environment; adjust it to your taste. So your code should look like:
require 'open3'
cmd = "\"C:\\Program Files\\Git\\bin\\git.exe\""
Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
puts "stdout: #{stdout.read}"
puts "\n\n"
puts "stderr: #{stderr.read}"
end
If you have command line parameters to pass to git, you'd do it like this:require 'open3'
cmd = "\"C:\\Program Files\\Git\\bin\\git.exe\" --version"
Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
puts "stdout: #{stdout.read}"
puts "\n\n"
puts "stderr: #{stderr.read}"
end
Executing the top command using Open3 in ruby
You can see your problem by printing block parameter e
:
The error should be like this:
This is common when trying to runtop: failed tty get
top
in non-interactive mode. To override this, you need the -b
option of top
.-b :Batch-mode operation
Starts top in Batch mode, which could be useful for sending output from top to other programs or to a file. In this mode, top will not accept input and
runs until the iterations limit you've set with the `-n' command-line option or until killed.
command = 'top -bn 1'
is ok then.Also there are many ways for system calling in ruby, check them here.
Related Topics
Activemodel::Validations on Anonymous Class
Cucumber Re-Run Failed Scenarios Automatically with a Tag
Why Slicing The Params Hash Poses a Security Issue on Mass-Assignment
How to Handle Omniauth Callbacks in Multiple Environments
Interpolation Within Single Quotes
Access an Instance Variable from Child Classes
How to Host Gem in Github and Use It
Ruby 1.9 How to Convert Array to String Without Brackets
How to Get Content from a Website Using Ruby/Rails
Rails Application Helper Didn't Support Chinese Characters
Automatically Run Rspec When Plain-Old Ruby (Not Rails) Files Change
Ruby a Clever Way to Execute a Function on a Condition
Truncate String When It Is Too Long
How to Use Rspec to Test That a Model Using Paperclip Is Validating The Size of an Uploaded File
Activestorage Issue to Set Record_Type on Active_Storage_Attachments
Overriding Instance Variable Array's Operators in Ruby
When Is The Enumerator::Yielder#Yield Method Useful
When Is a Block or Object That Is Passed to Hash.New Created or Run