package PGF::Utilities::RunProcess; =head1 NAME PGF::Utilities::RunProcess; General utility module to run an external process and return the STDOUT, STDERR and exit code and process id. =head1 VERSION $Revision: 1.12 $ $Date: 2009-08-26 17:18:40 $ =head1 SYNOPSIS use PGF::Utilities::RunProcess qw(runProcess); my %processInfo = runProcess( 'myProgram', { -stdout=>1 # capture stdout (default=1), -stderr=>1 # capture stderr (default=1), -stdoutFile=>filename (redirects stdout to specified file, std out string will not be return) -stderrFile=>filename (redirects stderror to specified file, std error string will not be return) -checkExecutable=>1 # check if command is executable (default=1) -verbose=>1 # prints stdout and stderr to screen (default=0) } # note that this argument is optional. ); print "exitCode=$processInfo{exitCode}\n"; print "stdout=$processInfo{stdoutMessage}\n"; print "stderr=$processInfo{stderrMessage}\n"; print "processId=$processInfo{pid}\n"; =head1 DESCRIPTION This module exports the method runProcess to the application's namespace. The return value is a hash containing the following key/value pairs: exitCode=exit code of the program that was ran. stdoutMessage=string containing the STDOUT message. stderrMessage=string containing the STDERR message. pid=process id of the program that was ran. =head1 AUTHOR(S) Stephan Trong =head1 HISTORY =over =item * S.Trong - 06/13/2006 - initial version =back =cut use strict; use warnings; use IPC::Open3; use Sys::Hostname; use Carp; use Carp qw(cluck); use IO::Select; require Exporter; our @ISA = qw(Exporter); our @EXPORT_OK = qw( runProcess ); #============================================================================# sub runProcess { my $command = shift; my $param = shift; # reference to hash my %params = defined $param ? %$param : (); # optional %params may be: # -stdout=>0|1 (default=1) # -stderr=>0|1 (default=1) # -stdoutFile=>filename (redirects stdout to specified file, std out string will not be return; optional) # -stderrFile=>filename (redirects stderror to specified file, std error string will not be return; optional) # -checkExecutable=>0|1 (default=1) # -verbose=>0|1 (default=0) my $captureStdOut = defined $params{-stdout} ? $params{-stdout} : 1; my $captureStdErr = defined $params{-stderr} ? $params{-stderr} : 1; my $checkExecutable = defined $params{-checkExecutable} ? $params{-checkExecutable} : 1; my $verbose = defined $params{-verbose} ? $params{-verbose} : 0; my $stdoutMessage = ""; my $stderrMessage = ""; local( *INPUT, *OUTPUT, *ERROR ); # Check if program exists and is executable. # if ( $checkExecutable ) { my $executable = (split /\s+/, $command)[0]; unless (-f $executable && -x $executable) { my $errorMessage = "could not exec $executable: " . "file does not exist, is not a regular file, or is not executable"; return ( exitCode=>-1, stdoutMessage=>$errorMessage, stderrMessage=>$errorMessage, pid=>0, ); } } # If -stdoutFile is specified, then create filehandle for output file. # if ( defined $params{-stdoutFile} ) { open STDOUT_FH, ">$params{-stdoutFile}" or confess "ERROR: failed to create file $params{-stdoutFile}.\n"; } # If -stderrFile is specified, then create filehandle for output file. # if ( defined $params{-stderrFile} ) { open STDERR_FH, ">$params{-stderrFile}" or confess "ERROR: failed to create file $params{-stderrFile}.\n"; } # Run process. # my $processId = open3(\*INPUT, \*OUTPUT, \*ERROR, $command); # Get STDOUT and STDERR messages. Using IO::Select to get STDERR and STDOUT # messages from process. Need to do this in order to bypass a potential # deadlock situation caused when the STDOUT or STDERR outputs are large. # my $selector = IO::Select->new(); $selector->add(\*ERROR, \*OUTPUT); while (my @ready = $selector->can_read) { foreach my $fh (@ready) { my $output = ""; my $sysreadResult = sysread($fh, $output, 1024); if (fileno($fh) == fileno(ERROR)) { if ( defined $params{-stderrFile} ) { print STDERR_FH $output; } else { $stderrMessage .= $output if $captureStdErr; } print " $output" if length $output && $verbose; } elsif (fileno($fh) == fileno(OUTPUT)) { if ( defined $params{-stdoutFile} ) { print STDOUT_FH $output; } else { $stdoutMessage .= $output if $captureStdOut; } print " $output" if length $output && $verbose; } $selector->remove($fh) if $sysreadResult == 0; } } close(INPUT); close(OUTPUT); close(ERROR); close STDOUT_FH if defined $params{-stdoutFile}; close STDERR_FH if defined $params{-stderrFile}; # Perform blocking wait until process completes. # waitpid($processId, 0); my $exitCode = $?>>8; return ( exitCode=>$exitCode, stdoutMessage=>$stdoutMessage, stderrMessage=>$stderrMessage, pid=>$processId, ); } #============================================================================# 1;