How to get STDERR in Perl from a command executed in pipe with su -c

六月ゝ 毕业季﹏ 提交于 2019-12-14 02:24:54

问题


I'm trying to capture the output of the command executed as a different user using:

my $command = qq(sudo su - <username> -c '/usr/bin/whatever');
my $pid = open $cmdOutput, "-|", $command;

How can I capture the STDERR of /usr/bin/whatever?

I tried

$pid = open $cmdOutput, "-|", $command || die " something went wrong: $!";

but it looks like this is capturing the possible errors of "open" itself.

I also tried

my $command = qq(sudo su - <username> -c '/usr/bin/whatever' 2>/tmp/error.message);

which will redirect the STDERR to the file, which I can parse later, but I wanted some more straightforward solution.

Also, I only want to use core modules.


回答1:


This is covered thoroughly in perlfaq8. Since you are using a piped open, the relevant examples are those that go by open3 from the core IPC::Open3 module.

Another option is to use IPC::Run for managing your processes, and the pump function will do what you need. The IPC::Open3 documentation says for IPC::Run

This is a CPAN module that has better error handling and more facilities than Open3.

With either of these you can manipulate STDOUT and STDERR separately or together, as needed. For convenient and complete output capture also see Capture::Tiny.

Other than 2>output redirection, there are no more elementary methods for the piped open.


If you don't mind mixing the streams or losing STDOUT altogether, another option is

my $command = 'cmd 2>&1 1>/dev/null'          # Remove 1>/dev/null to have both
my $pid = open my $cmdOutput, "-|", $command;

while (<$cmdOutput>) { print }                # STDERR only

The first redirection merges STDERR stream with STDOUT so you get them both, and mixed (with STDOUT subject to buffering, thus things may well come out of order). The second redirect sends the STDOUT away so with it in place you read only the command's STDERR from the handle.


The question is about running an external command using open but I'd like to mention that the canonical and simple qx (backticks) can be used in the same way. It returns the STDOUT so redirection just like above is needed to get STDERR. For completeness:

my $cmd = 'cmd_to_execute';
my $allout = qx($cmd 2>&1);              # Both STDOUT and STDERR in $out, or
my $stderr = qx($cmd 2>&1 1>/dev/null);  # Only STDERR
my $exit_status = $?;

The qx puts the child process exit code (status) in $?. This can then be inspected for failure modes; see a summary in the qx page or a very thorough discussion in I/O operators in perlop.

Note that the STDERR returned this way is from the command, if it ran. If the command itself couldn't be run (for a typo in command name, or fork failed for some reason) then $? will be -1 and the error will be in $!.




回答2:


As suggested by zdim I used the IPC::Open3 module for the matter and I've got something like this doing the job for me

 $instanceCommand = qq(sudo su - <username> -c '<command>');
 my ($infh,$outfh,$errfh,$pid);
 $errfh = gensym();
 $pid = open3($infh, $outfh, $errfh, $instanceCommand);
 my $sel = new IO::Select;
 $sel->add($outfh,$errfh);
 while (my @ready = $sel->can_read){
      foreach my $fh (@ready){
           my $line =<$fh>;
           if (not defined $line){
                $sel->remove($fh);
                next;
                }
           if ($fh == $outfh){
                chomp($line);
                #<----- command output processing ----->
                }
           elsif ($fh == $errfh){                                                                                      
                chomp $line;
                #<----- command error processing ----->
                }
           else {
                die "Reading from something else\n";
                }
            }
      }
waitpid($pid, 0);

Maybe not completely bullet proof, but its working fine for me. Even whilst executing funny cascaded script as < command > .




回答3:


The desired destination, opened for writing, could be dup()'ed to FD #2



来源:https://stackoverflow.com/questions/38218998/how-to-get-stderr-in-perl-from-a-command-executed-in-pipe-with-su-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!