Friday, December 4, 2009

Recipe 20.9. Capturing the Output and Error Streams from a Unix Shell Command










Recipe 20.9. Capturing the Output and Error Streams from a Unix Shell Command



Problem


You want to run an
external program as in Recipe 20.8, but you also want to capture the standard error stream. Using
popen
only gives you access to the standard output.




Solution


Use the open3 library in the Ruby standard library. Its popen3 method takes a code block, to which it passes three IO streams: one each for standard input, output, and error.


Suppose you perform the Unix ls command to list a nonexistent directory. ls will rightly object to this and write an error message to its standard error stream. If you invoked ls with IO.popen or the %x{} construction, that error message is passed right along to the standard error stream of your Ruby process. You can't capture it or suppress it:



%x{ls no_such_directory}
# ls: no_such_directory: No such file or directory



But if you use popen3, you can grab that error message and do whatever you want with it:



require 'open3'

Open3.popen3('ls -l no_such_directory') { |stdin, stdout, stderr| stderr.read }
# => "ls: no_such_directory: No such file or directory\n"





Discussion


The same caveats in the previous recipe apply to the IO streams returned by
popen3
. If you're running a command that accepts data on standard input, and you read
from stdout before closing stdin, your process will hang.


Unlike IO.popen, the
popen3
method is only implemented on
Unix systems. However, the win32-open3 package (part of the Win32Utils project) provides a popen3 implementation.




See Also


  • Recipe 20.8, "Driving an External Process with popen"

  • Like many other Windows libraries for Ruby, win32-open3 is available from http://rubyforge.org/projects/win32utils













No comments: