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


Strings

Numbers

Date and Time

Arrays

Hashes

Files and Directories

Code Blocks and Iteration

Objects and Classes8

Modules and Namespaces

Reflection and Metaprogramming

XML and HTML

Graphics and Other File Formats

Databases and Persistence

Internet Services

Web Development Ruby on Rails

Web Services and Distributed Programming

Testing, Debugging, Optimizing, and Documenting

Packaging and Distributing Software

Automating Tasks with Rake

Multitasking and Multithreading

User Interface

Extending Ruby with Other Languages

System Administration



Ruby Cookbook
Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net