Driving an External Process with popen

Problem

You want to execute an external command in a subprocess. You want to pass some data into its standard input stream, and read its standard output.

Solution

If you don care about the standard input side of things, you can just use the %x{} construction. This runs a string as a command in an operating system subshell, and returns the standard output of the command as a string.

	%x{whoami} # => "leonardr
"
	puts %x{ls -a empty_dir}
	# .
	# ..

If you want to pass data into the standard input of the subprocess, do it in a code block that you pass into the IO. popen method. Heres IO.popen used on a Unix system to invoke tail, a command that prints to standard output the last few lines of its standard input:

	IO.popen(	ail -3, 
+) do |pipe|
	 1.upto(100) { |i| pipe >> "This is line #{i}.
" }
	 pipe.close_write
	 puts pipe.read
	end
	# This is line 98.
	# This is line 99.
	# This is line 100.

Discussion

IO.popens pawns a subprocess and creates a pipe: an IO stream connecting the Ruby interpreter to the subprocess. IO.popen makes the pipe available to a code block, just as File.open makes an open file available to a code block. Writing to the IO object sends data to the standard input of the subprocess; reading from it reads data from its standard output.

IO.popen takes a file mode, just like File.open. To use both the standard input and output of a subprocess, you need to open it in read-write mode ("r+").

A command that accepts standard input won really start running until its input stream is closed. If you use popen to run a command like tail, you must call pipe. close_write before you read from the pipe. If you try to read the subprocess standard output while the subprocess is waiting for you to send it data on standard input, both processes will hang forever.

The %{} construct and the popen technique work on both Windows and Unix, but scripts that use them won usually be portable, because its very unlikely that the command you e running exists on all platforms.

On Unix systems, you can also use popen to spawn a Ruby subprocess. This is like calling fork, except that the parent gets a read-write filehandle thats hooked up to the standard input and output of the child. Unlike with Kernel#fork (but like Cs implementation of fork), the same code block is called for the parent and the child. The presence or absence of the filehandle is the only way to know whether you e the parent or the child:

	IO.popen(-, 
+) do |child_filehandle|
	 if child_filehandle
	 $stderr.puts "I am the parent: #{child_filehandle.inspect}"
	 child_filehandle.puts 404
	 child_filehandle.close_write
	 puts "My child says the square root of 404 is #{child_filehandle.read}"
	 else
	 $stderr.puts "I am the child: #{child_filehandle.inspect}"
	 number = $stdin.readline.strip.to_i
	 $stdout.puts Math.sqrt(number)
	 end
	end
	# I am the child: nil
	# I am the parent: #
	# My child says the square root of 404 is 20.0997512422418

See Also

  • Recipe 20.1, "Running a Daemon Process on Unix"
  • Recipe 20.9, " Capturing the Output and Error Streams from a Unix Shell Command"
  • Recipe 20.10, "Controlling a Process on Another Machine"


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