Dieser Post wurde aus meiner alten WordPress-Installation importiert. Sollte es Darstellungsprobleme, falsche Links oder fehlende Bilder geben, bitte einfach hier einen Kommentar hinterlassen. Danke.
IO::CaptureOutput is a great module but provides limited control. Here is a quick hack to replace the capture_exec function while getting more control about what is being done.
The executed script may have a huge output which ended up filling a variable when using IO::Capture from CPAN but that variable was never used.
Here is my solution capturing standard error and the external command's exit code but ignoring standard output:
use IPC::Open3 qw(open3);use Symbol qw(gensym);use Fcntl;use POSIX ":sys_wait_h";It's mostly copy and paste stuff from IPC::Open3, Perldoc SELECT and a Perl cookbook snippet. There are some uncommon things like pre-preparing the $err handle, $rout = $rin syntax within select or usage of sysread. Many of them are explained in the documentation linked above, the others are mistakes I made or might have better alternates (remember TIMTOWTDI).sub capture_exec {
# Create handles and launch child task my ($wtr, $rdr); my $err = gensym; my $pid = open3($wtr, $rdr, $err, @_); close $wtr; # Child won't expect STDIN
# Set child's STDOUT and STDERR to non-blocking mode. for my $fh ($rdr, $err) { my $flags = ''; fcntl($fh, F_GETFL, $flags) or die "Couldn't get flags for HANDLE : $!\n"; $flags |= O_NONBLOCK; fcntl($fh, F_SETFL, $flags) or die "Couldn't set flags for HANDLE: $!\n"; }
# Prepare vectors for select my $rin = ''; vec($rin, fileno($rdr), 1) = 1; vec($rin, fileno($err), 1) = 1;
# Prepare STDERR buffer my $errors;
while (1) {
# Wait until the child has data to read for us or 1 sec. timeout my $rout; if (select($rout = $rin, undef, undef, 1)) { my $buffer; # Buffer for reading one data block
# Discard any STDOUT data while (sysread($rdr, $buffer, 65535) == 65535) { } # Read 64k blocks as long as we get 64k
# Read STDERR 64k blocks and push then onto the STDERR buffer while (1) { sysread($err, $buffer, 65535); $errors .= $buffer; last if length($buffer) < 65535; } }
# Clean up child process zombie(s) waitpid -1, WNOHANG;
last unless kill 0, $pid; # Child is still running }
return \$errors, $? >> 8;}
2 Kommentare. Schreib was dazu-
dagolden
7.03.2012 4:27
Antworten
-
dagolden
12.03.2012 15:46
Antworten
use Capture::Tiny 'capture_stderr'; ($stderr, $exit) = capture_stderr { system(@cmd) };
Actually, that snippet above won't avoid passing through lots of output. If you really want to discard output, pass a custom capture() handle to /dev/null and throw away the first return value (which will be empty anyway):
use Capture::Tiny 'capture'; use File::Spec; use IO::File; (undef, $stderr, $exit) = capture { system(@cmd) } stdout => IO::File->new( File::Spec->devnull );