Ruby-Open3.Popen3/How to Print The Output

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

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:

top: failed tty get

This is common when trying to run 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



Leave a reply



Submit