Processing the output of a hung process
I'm wanting to run some test executables with a perl script, parse their STDOUT, and return an exit value depending on what the STDOUT contained. I'm not very familiar with perl, so it's a been a bit tough.
The problem I'm running into is ensuring that my perl script receives whatever output it can even if the test executables crash or hang.
At the moment, I'm using alarm to time out. If it hangs, I'd like to just take whatever data I can and continue onwards to parsing. Unfortunately, with my current execution method, if it hangs I get no data and the zombie process lives forever.
My (probably naive) version of getting the test output is as so.
#!/usr/bin/perl
use strict;
use warnings;
my @output;
eval {
local $SIG{ALRM} = sub {die "alarm\n"};
alarm 15;
@output = `testExecutable`;
alarm 0;
};
if ($@) {
die unless $@ eq "alarm\n";
print "timed out\n";
}
else {
print "didn't time out\n";
}
print @output;
Basically, I need to execute the testE开发者_如何学Goxecutable in a way that will allow me to access any data it's outputted before the alarm goes off, and then kill the testExecutable process in my alarm handler.
If necessary, I can modify the test executables. Looking around, it seems buffering might be of some concern.
You problem is that using the backquote operator only populates your perl variable upon completion. So instead you should read from a perl pipe. So something like this:
#!/usr/bin/env perl
use strict;
use warnings;
my @output;
eval {
local $SIG{ALRM} = sub {die "alarm\n"};
alarm 15;
open my $pipe_handle, '-|', 'testExecutable' or die "open error: $!";
while (my $line = <$pipe_handle>) {
push @output, $line;
}
close $pipe_handle;
alarm 0;
};
if ($?) {
print "testProgram failed\n";
} elsif ($@) {
die unless $@ eq "alarm\n";
print "timed out\n";
} else {
print "didn't time out\n";
}
print @output;
Keep in mind that there may be some bufferring going on, so you could still miss out on some of the output.
Edit: Added in check for $?
to check status of child program
Write the output of the external command to a file, and read the file when the process is complete (normally or timing out). Like Sodved said, it's best if you can get the external program to flush its output frequently.
my $output_file = '/tmp/foo';
eval {
local $SIG{ALRM} = sub {die "alarm\n"};
alarm 15;
system("testExecutable > $output_file 2>&1");
alarm 0;
};
# whether successful or not, there could be output in $output_file ...
open my $fh, '<', $output_file;
@output = <$fh>;
close $fh;
unlink $output_file;
精彩评论