user974896
user974896

Reputation: 1813

How can I run a process from Perl and capture both output and return code?

I would like to run a separate process in Perl (a command line PHP script). I would like to capture this processes output AND return code. Optionally I would like to be able to terminate the process and move on if the process takes longer than N seconds. What should I look into?

Upvotes: 0

Views: 833

Answers (6)

PlasmaBinturong
PlasmaBinturong

Reputation: 2274

Without considering the optional part of your question, you can obtain the output of a command within backticks, and its return value in the special variable $?.

my $output = `shell_command`;
die ("shell_command failed!\n") if ($?);

Upvotes: 0

Unos
Unos

Reputation: 1361

This is a frequently asked question. Have a look at this Perl FAQ too. It basically explains the way to run external commands in Perl.

Upvotes: 1

Tanktalus
Tanktalus

Reputation: 22254

Personally, I find the easiest is AnyEvent with AnyEvent::Util::run_cmd and a timer. There is no requirement here to run your entire program under an event loop, I regularly go in and out of the event loops with this solution today. (The following isn't warnings-safe.)

use AnyEvent;
use AnyEvent::Util;

my ($stdout, $stderr, $pid);

my $cv = AnyEvent::Util::run_cmd [ $^X, -e => '$|++; sleep 2; print "foo\n"; sleep 2' ],
    '>'  => \$stdout,
    '2>' => \$stderr,
    '$$' => \$pid,
;

my $w = AnyEvent->timer(after => 3, interval => 0, cb => sub { print "killing!\n"; kill SIGINT => $pid });

my $rc = $cv->recv();
$w = undef; # end the timer.

print "got rc = [$rc]\n";
print "stdout = [$stdout]\n";
print "stderr = [$stderr]\n";

Upvotes: 2

Galimov Albert
Galimov Albert

Reputation: 7357

You can use piped open() to get all 3 goals:

my $pid = open(F, "cat /etc/passwd |") || die "Cant open pipe: $!";
eval {
     # Give 30 secs to read output:
     local $SIG{ALRM} = sub { kill(9, $pid); die "alarm\n"; }; # NB: \n required
     alarm(30);

     # Read output:
     my $line = <F>;
     alarm(0); # Remove alarm

     #..do job with $line
};
# Get errorcode:
close(F);
print "Exit status: $?\n";
printf "Exit value(return code): %d\n", $?>>8;
print "Killed by timeout\n" if ($? & 127)==9;

Upvotes: 1

ysth
ysth

Reputation: 98398

use IPC::System::Simple qw/EXIT_ANY $EXITVAL capture/;

my $output = capture(EXIT_ANY, 'some command');
print "exit value was $EXITVAL\n";

EXIT_ANY tells capture to allow any exit value; you can instead list allowed ones (capture([0,1,42], ...)) and capture will throw an exception if another is encountered. By default (if just a command is passed to capture) any non-zero exit value results in an exception.

Upvotes: 1

gpojd
gpojd

Reputation: 23065

You can try IPC::Run.

IPC::Run - system() and background procs w/ piping, redirs, ptys (Unix, Win32)

Upvotes: 2

Related Questions