Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Running a shell command from Ruby: capturing the output while displaying the output?

Tags:

bash

ruby

I have a problem.

I want to run a ruby script from another ruby script and capture it's output information while letting it output to the screen too.

runner

#!/usr/bin/env ruby
print "Enter your password: "
password = gets.chomp
puts "Here is your password: #{password}"

The script file that I run:

start.rb

output = `runner`
puts output.match(/Here is your (password: .*)/).captures[0].to_s

As you see here there is a problem.

In the first line of start.rb the screen is empty.

I cannot see the "Enter your password: " in runner.

Is there a way to display the output of the runner script before it's finished, and still let me capture it to a string so I can process the information, eg. using match like in this example?

like image 515
never_had_a_name Avatar asked Sep 10 '10 07:09

never_had_a_name


3 Answers

runner.rb

STDOUT.print "Enter your password: "
password = gets.chomp
puts "Here is your password: #{password}"

Note STDOUT.print

start.rb

require "stringio"

buffer = StringIO.new
$stdout = buffer

require "runner"

$stdout = STDOUT
buffer.rewind

puts buffer.read.match(/Here is your (password: .*)/).captures[0].to_s

output

Enter your password: hello
password: hello

Read more...

I recently did a write-up on this here: Output Buffering with Ruby

like image 107
maček Avatar answered Oct 16 '22 02:10

maček


Try this:

rd, wr = IO::pipe
pid = Process.fork do  
  $stdout.reopen(wr)
  rd.close
  exec("command")
end
wr.close
rd.each do |line|  
  puts "line from command: #{line}"
end
Process.wait(pid)

Similar if you want to capture stderr. If you need to capture both it would a bit more difficult (Kernel.select?)

Edit: Some explanation. This is an ancient Unix procedure: pipe + fork + calls to dup2 (reopen) depending on what you want. In a nutshell: you create a pipe as a means of communication between child and parent. After the fork, each peer close the pipe's endpoint it does not use, the child remaps (reopen) the channel you need to the write endpoint of the pipe and finally the parent reads on the read channel of the pipe.

like image 40
tokland Avatar answered Oct 16 '22 01:10

tokland


For script independent output logging you might want to enable it from the terminal emulator (shell container). screen -L OR xterm -l This will capture all output produced by any shell or program running inside the emulator, including output generated by your ruby scripts.

like image 20
usbdongle Avatar answered Oct 16 '22 03:10

usbdongle