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
- Recipe 20.8, "Driving an External Process with popen"
- Recipe 21.9, "Reading a Password," shows how to obtain a password without echoing it to the screen
Категории