The perl script below invokes unix find using
the open command.
STDOUT goes into the CMD filehandle and STDERR, if any, goes into a temporary file on disk.
STDERR will result if the user makes a mistake on the command line. But even if the user makes a mistake, it is still possible that STDOUT could contain useful data, as in my example below.
Thus we want two separate variables in the perl script, holding respectively STDOUT and STDERR from the unix command.
In the current script, STDERR is only obtained by first printing it to a file on disk and subsequently reading the file.
Is there a way to capture both STDOUT and STDERR in separate variables without the intermediate step of printing STDERR to a physical file on disk? Can it somehow be printed directly to a perl filehandle or a perl variable?
I am aware that one can use File::Find in perl instead of an explicit system call. But open can be applied to other unix commands, so an answer to the current question could be applied to any number of unix commands.
> cat z.pl
#!/usr/bin/env perl
use strict; use warnings;
use Data::Dumper qw(Dumper); $Data::Dumper::Sortkeys = 1;
my$work={};
$work->{tmpfile}='./z';
$work->{command}=join(' ',
'find -s ',
@ARGV,
'2>',
$work->{tmpfile},
);
open CMD, '-|', $work->{command} or die $work->{command},' cannot open';
$work->{return}{open}=$?;
my@result;
while(<CMD>)
{
chomp;
push@result,$_;
}
close CMD;
$work->{return}{close}=$?;
if(-s $work->{tmpfile})
{
open IN,'<',$work->{tmpfile} or die 'cannot read ',$work->{tmpfile},;
local$/=undef;
$work->{stderr}=<IN>;
close IN;
}
print Dumper $work;
print Dumper \@result;
> ./z.pl . piggy
$VAR1 = {
'command' => 'find -s . piggy 2> ./z',
'return' => {
'close' => 256,
'open' => 0
},
'stderr' => 'find: piggy: No such file or directory
',
'tmpfile' => './z'
};
$VAR1 = [
'.',
'./.z.pl.swp',
'./body',
'./body~',
'./snap',
'./title',
'./z',
'./z.pl',
'./z.pl~'
];
>
IPC::Run makes this easy.
The child inherits STDIN in the above program. You could use the following if that's undesirable:
But if you're interested in learning the system calls involved, read on.
/dev/null._exit( 255 ). (The exit code doesn't matter.)dup2to copy the handle to/dev/nullonto fd 0._exit( 255 ). (The exit code doesn't matter.)/dev/null.dup2to copy the write end the second pipe onto fd 1._exit( 255 ). (The exit code doesn't matter.)dup2to copy the write end the second pipe onto fd 2._exit( 255 ). (The exit code doesn't matter.)execthe program._exit( 255 ). (The exit code doesn't matter.)$error_msg.$error_msg.$error_msgis defined,$error_msgcontains the reason.selectbitfield for second and third pipe.selectto wait for data.sysreadto read from the ready handle.sysreadreturned -1),sysreadreturned zero),waitpidto wait for the child to exit.IPC::Open3's
open3can handle steps 1 to 12 for you.IO::Select can simplify steps 13 to 15 for you.