Using perl open3 to pipe data through gnuplot
open3 is useful for piping through some program, and back. This is handy in many situations - personally I find it handy for turning database tables into graphs with gnuplot . It is very similar to open 2 with the added advantage of being able to capture standard error messages
You want to use open3 when you're doing something like this;
- You have a program which takes something on it's standard input, and changes into something on standard output, and may generate information on standard error. The simplest example of such a program is something like sort, though, in my example, I've used gnuplot .
- You have a bunch of stuff you want to feed to the program.
- In return, you want to get a whole bunch of stuff, after having being passed through the program
The following example takes an array and generates a PNG graph directly from it, using gnuplot.
I've omitted code to check for sanity (For example, checking that $xArray and $yArray are the same length). You should also check out this book if you want more detailed perl examples.
print "Content-type:image/png\n\n";
# Many examples for open3 don't include creation of the filehandles.
# You aren't meant to pass them in without creating them first!
# IMPORTANT - open2 and open3 have their parameters in a slightly different order.
# Be sure and look if you're switching between them! ;)
local (*READ, *WRITE, *ERROR);
my $pid = open3( \*WRITE, \*READ, \*ERROR, "gnuplot" );
# We could have also done it like this
# my ($readFH, $writeFH, $errorFH);
# my $pid = open3($writeFH, $readFH, $errorFH, "gnuplot");
print WRITE "set terminal png small\n";
print WRITE "set nokey\n";
print WRITE "set grid\n";
print WRITE "set size 0.5, 0.5\n";
print WRITE "set data style points\n";
print WRITE "set title \'$title\'\n";
print WRITE "set xlabel \'$xLabel\'\n";
print WRITE "set ylabel \'$yLabel\'\n";
print WRITE "plot \'-'\n";
foreach my $index (0..$#xArray)
{
print WRITE "$xArray[$index]\t$yArray[$index]\n";
}
close(WRITE);
# Contrasting this with the open2 example, we need to
# add in this wacky select code. The reason is because there
# are two streams (STDOUT and STDERR) which could have data
# and we can't block and wait on either of them.
# Instead, we monitor both, using select.
my $sel = IO::Select->new();
$sel->add(\*ERROR, \*READ);
while(my @ready = $sel->can_read)
{
foreach my $handle (@ready) {
if (fileno($handle) == fileno(ERROR))
{
# Gnuplot has printed something on standard error
exit;
}
else
{
my ($count, $data);
$count = sysread($_, $data, 1024);
# Business as usual.
print $data;
}
$sel->remove($handle) if eof($handle);
}
}
close(READ);
close(ERROR);
Important note!
There are three important things to remember when using open3 . They are (listed in order of importance):- open3 does not play well with mod_perl.
- open3 does not play well with mod_perl.
- open3 does not play well with mod_perl.