NAME
Overview of IO::Capture
Module, and classes derived from it.
DESCRIPTION
The modules in this distribution are designed to allow you to capture and process output sent to STDOUT and/or STDERR.
I initially created the modules to use in building module tests. I wanted to be able to intentionally cause errors, and insure the module responded correctly. E.g., Call a class method without a required argument. Using IO::Capture keeps the user from seeing these intentional errors when running 'make test'.
I have also found useful on occasion in Perl Tk apps, where I wanted to capture output from a Perl module I was using. I could then capture, then put the text into a log or message window.
Note: None of the modules currently distributed will capture from the 'system' Perl function, or the like. It could be done, but generally, if you would like to capture from a system command, you don't need this module, just use the backticks operators.
my $output = '/usr/bin/ls';
They are small, lightweight modules. Instead of designing in a lot of features, we designed it to be easily reusable and adaptable. A module can be quickly built, that incorporates custom methods, but reuses all existing features of one of the derived classes. See the section on "ADDING FEATURES" Or, if you need to change the actual capture mechanism, "WRITING YOUR OWN DERIVED CLASS". (Don't worry, it's a piece of cake)
DERIVED CLASSES
There are several classes derived from IO::Capture
.
IO::Capture::Stdout
Module to capture STDOUT
from program. See IO::Capture::Stdout.
IO::Capture::Stderr
Module to capture STDERR
from program. See IO::Capture::Stderr.
IO::Capture::ErrorMessages
Module to capture STDERR
with an added handler for SIG(__WARN__)
. See IO::Capture::ErrorMessages.
If you would like to add features to any of these, or build your own module using IO::Capture
as a base, read on.
ADDING FEATURES
If one of these modules take care of your problem, install it and have fun!
But lets say you would like to add a feature to one of the derived classes, say IO::Capture::Stdout. No need to re-write the whole module, just use it as the base, and write your one feature. Here is a somewhat simplified example.
#
# Example module to add a grep_it method
#
# Give your package a name
package MyPackage;
#use IO:Capture:Stdout as the base
use base 'IO::Capture::Stdout';
#define your method
sub grep_it {
my $self = shift;
my $string = shift;
my @found_lines;
# Making a ref to the array makes it easier to read :-)
my $arrayref = \@{$self->{'IO::Capture::messages'}};
for my $line (@$arrayref) {
push @found_lines, $line if $line =~ /$string/;
}
return wantarray ? @found_lines : shift @found_lines;
}
1;
Using it in this script
#!/usr/sbin/perl
use strict;
use warnings;
use MyPackage;
my $capture = MyPackage->new();
$capture->start;
print "The quick brown fox jumped over ...";
print "garden wall";
print "The quick red fox jumped over ...";
print "garden wall";
$capture->stop;
for my $line ($capture->grep_it("fox")) {
print "$line\n";
}
Results in
$ grep_it
The quick brown fox jumped over ...
The quick red fox jumped over ...
WRITING YOUR OWN DERIVED CLASS
Before starting your own sub-class, be sure to read through IO::Capture. Pay special attention to the internal methods that are only defined as abstract methods in IO::Capture
. For examples, look at the sub-classes included with this distribution. (IO::Capture::Stdout
, IO:Capture::Stderr
, IO:Capture::ErrorMessages
) You can start by copying one of these and using it as a templet. They have the required private methods defined already, and you may very well be able to use them as is. Change any methods, and add any new ones, as needed.
For example, here is a commented copy of IO::Capture::Stderr
.
#
# Example module using abstract class IO::Capture
#
# Change this to give your class it's own name
package IO::Capture::Stderr;
# Make IO::Capture the base class
use base qw/IO::Capture/;
# If using included utility module in '_start()'
use IO::Capture::Tie_STDx;
# Override the three abstract methods needed to make a valid
# module. See IO::Capture manpage
# 1) _start - Starts the data capture. Is run from public method
# start();
#
# 2) _retrieve_captured_text() - Move the captured text into the
# object hash key, "IO::Capture::messages". Called by public method
#
# 3) _stop - Stop the data capture. Called by public method 'stop()'
# after private method '_retrieve_captured_text()' returns.
#
sub _start {
tie *STDERR, "IO::Capture::Tie_STDx";
}
sub _retrieve_captured_text {
my $self = shift;
# making a reference to it makes it more readable ;-)
my $messages = \@{$self->{'IO::Capture::messages'}};
@$messages = <STDERR>;
}
sub _stop {
untie *STDERR;
return 1;
}
1;
Lets say you don't want to capture all the text. You just want to grab the lines that have the word "Error" in them. The only thing you need to change is _retrieve_captured_text. (Besides the package name)
Something like:
sub _retrieve_captured_text {
my $self = shift;
# making a reference to it makes it more readable ;-)
my $messages = \@{$self->{'IO::Capture::messages'}};
while (<STDERR>) {
push @$messages, $_ if /error/i;
}
}
Yes. You could do this easier by just using IO::Capture::Stderr
as the base and overriding _retrieve_captured_text
like in "ADDING FEATURES", but hey, we needed an easy example. :-)
AUTHORS
Mark Reynolds reynolds<at>sgi.com
Jon Morgan jmorgan<at>sgi.com
Note: Change <at
to 'at' sign.>
COPYRIGHT
Copyright (c) 2003, Mark Reynolds. All Rights Reserved. This module is free software. It may be used, redistributed and/or modified under the same terms as Perl itself.