1

I am currently attempting to figure out a way to call a Perl script from Ruby and have it output as if I was in the terminal and would allow me to provide input if it is needed.

I have figured out how I can do this and get the input after the fact but because the Perl script is still running, I am not able to run anything else.

I should note that I can not edit the Perl scripts. These scripts are being provided and this Ruby script is being made to make the process of running all of the Perl scripts easier and ensuring they are in the right order.

upgradestatus =  `#{upgradearray[arraylocation]}`

This would be the relevant part my code for this. I have attempted a few other variations of how to do this but I get the same situation every time. When the script starts running it requires input so it just sits there.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Jon Heckman
  • 420
  • 2
  • 7
  • 18
  • Your question needs more clarity. Perhaps you should show us what you've done or post psuedo code so we can see what you expect as input, output and control flow. – sunnyrjuneja Jan 07 '13 at 19:14
  • Child processes will normally inherit their parent's handles, including STDIN. Is that good enough? – ikegami Jan 07 '13 at 19:15
  • Can you use something like [this](http://stackoverflow.com/questions/3159945/running-command-line-commands-within-ruby-script) to invoke perl from the command line? – sunnyrjuneja Jan 07 '13 at 19:16
  • I updated it slightly. Unless I am missing something from that link. Those are all different ways of calling the script but would not output as the script is running. – Jon Heckman Jan 07 '13 at 19:33

1 Answers1

0

You can't do what you want using backticks, %x or as a normal sub-shell, because they lack the ability to watch the output of the sub-command's output.

You could do it using Open3's popen2 or popen3 methods. They let you send to the STDIN stream for the called program, and receive data from the STDOUT. popen3 also lets you see/capture the STDOUT stream too. Unfortunately, often you have to send, then close the STDIN stream before the called program will return its information, which might be the case of the Perl scripts.

If you need more control, look into using Ruby's built-in Pty module. It's designed to let you talk to a running app through a scripting mechanism. You have to set up code to look for prompts, then respond to them by sending back the appropriate data. It can be simple, or it can be a major PITA, depending on the code you're talking to.

This is the example for the open command:

PTY.open {|m, s|
p m      #=> #<IO:masterpty:/dev/pts/1>
p s      #=> #<File:/dev/pts/1>
p s.path #=> "/dev/pts/1"
}

# Change the buffering type in factor command,
# assuming that factor uses stdio for stdout buffering.
# If IO.pipe is used instead of PTY.open,
# this code deadlocks because factor's stdout is fully buffered.
require 'io/console' # for IO#raw!
m, s = PTY.open
s.raw! # disable newline conversion.
r, w = IO.pipe
pid = spawn("factor", :in=>r, :out=>s)
r.close
s.close
w.puts "42"
p m.gets #=> "42: 2 3 7\n"
w.puts "144"
p m.gets #=> "144: 2 2 2 2 3 3\n"
w.close
# The result of read operation when pty slave is closed is platform
# dependent.
ret = begin
        m.gets          # FreeBSD returns nil.
    rescue Errno::EIO # GNU/Linux raises EIO.
        nil
    end
p ret #=> nil
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
  • Thank you for the help. This looks like it should work if I am understanding correctly. The input needed is just reacting to possible errors and confirming that the script should be run so that should be easy to program in. However when I try to run what you put in. I get "test.rb:1: uninitialized constant PTY (NameError)" – Jon Heckman Jan 07 '13 at 22:07
  • Maybe you should try `require 'pty'`? – the Tin Man Jan 07 '13 at 23:04
  • This seems like the solution should work but I was having a hard time getting it to work. However I figured out a solution that worked for me. I spawned the script. The script will be detectable with a simple sql command so I just have it looping and sleeping for a few seconds until it can no longer detect it before continuing. Not the most graceful solution but for speed so this script is usable and then I can update it later so it is fully automated. The best solution I was able to get working. – Jon Heckman Jan 09 '13 at 19:37