Scripting an External Program

Problem

You want to automatically control an external program that expects to get terminal input from a human user.

Solution

When you e running a program that only needs a single string of input, you can use IO.popen, as described in Recipe 20.8. This method runs a command, sends it a string as standard input, and returns the contents of its standard output:

def run(command, input=\) IO.popen(command, +) do |io| io.puts input io.close_write return io.read end end run wc -w, How many words are in this string? # => "7 "

This technique is commonly used to invoke a command with sudo, which expects the users password on standard input. This code obtains a users password and runs a command on his behalf using sudo:

print Enter your password for sudo: sudo_password = gets.chomp run(sudo apachectl graceful, user_password)

Discussion

IO.popen is a good way to run noninteractive commandscommands that read all their standard input at once and produce some output. But some programs are interactive; they send prompts to standard output, and expect a human on the other end to respond with more input.

On Unix, you can use Rubys standard PTY and expect libraries to spawn a command and impersonate a human on the other end. This code scripts the Unix passwd command:

require expect require pty print Old password: old_pwd = gets.chomp print " New password:" new_pwd = gets.chomp PTY.spawn(passwd) do |read,write,pid| write.sync = true $expect_verbose = false # If 30 seconds pass and the expected text is not found, the # response object will be nil. read.expect("(current) UNIX password:", 30) do |response| write.print old_pwd + " " if response end # You can use regular expressions instead of strings. The code block # will give you the regex matches. read.expect(/UNIX password: /, 2) do |response, *matches| write.print new_pwd + " " if response end # The default value for the timeout is 9999999 seconds read.expect("Retype new UNIX password:") do |response| write.puts new_pwd + " " if response end end

The read and write objects in the PTY#spawn block are IO objects. The expect library defines the IO#expect method found throughout this example.

See Also

Категории