Tuesday, December 15, 2009

Recipe 6.16. Redirecting Standard Input or Output










Recipe 6.16. Redirecting Standard Input or Output







Problem


You don't want the standard input, output, or error of your process to go to the
default IO objects set up by the Ruby interpreter. You want them to go to other filetype objects of your own choosing.




Solution


You can
assign any IO object (a File, a Socket, or what have you) to the global variables $stdin, $stdout, or $stderr. You can then read from or write to those objects as though they were the originals.


This short Ruby program demonstrates how to redirect the Kernel methods that print to standard output. To avoid confusion, I'm presenting it as a standalone Ruby program rather than an interactive irb session.[4]

[4] irb prints the result of each Ruby expression to $stdout, which tends to clutter the results in this case.



#!/usr/bin/ruby -w
# ./redirect_stdout.rb
require 'stringio'
new_stdout = StringIO.new

$stdout = new_stdout
puts "Hello, hello."
puts "I'm writing to standard
output."

$stderr.puts "#{new_stdout.size} bytes written to standard ouput so far."
$stderr.puts "You haven't seen anything on the screen yet, but you soon will:"
$stderr.puts new_stdout.string



Run this program and you'll see the following:



$ ruby redirect_stdout.rb
46 bytes written to standard output so far.
You haven't seen anything on the screen yet, but you soon will:
Hello, hello.
I'm writing to standard output.





Discussion


If you have any Unix experience, you know that when you run a Ruby script from the command line, you can make the shell redirect its standard
input, output, and error streams to files or other programs. This technique lets you do the same thing from within a Ruby script.


You can use this as a quick and dirty way to write errors to a file, write output to a StringIO object (as seen above), or even read input from a socket. Within a script, you can programatically decide where to send your output, or receive standard input from multiple sources. These things are generally not possible from the command line without a lot of fancy shell scripting.


The redirection technique is especially useful when you've written or inherited a script that prints text to standard output, and you need to make it capable of printing to any file-like object. Rather than changing almost every line of your code, you can just set $stdout at the start of your program, and let it run as is. This isn't a perfect solution, but it's often good enough.


The original input and output streams for a process are always available as the constants STDIN, STDOUT, and STDERR. If you want to temporarily swap one IO stream for another, change back to the "standard" standard output by setting $stdin = STDIN. Keep in mind that since the $std objects are global variables, even a temporary change affects all threads in your script.




See Also


Recipe 6.15, "Pretending a String Is a File," has much more information on StringIO













No comments: