4

In Perl, one way to read the STDOUT of a subprocess is to use open:

open(PIPE, "ls -l |");

I was looking for a more object-oriented way to do this, though, and I've been using IO::Pipe with some success. I want to detect errors, though, specifically if the command is not executable. I can't figure out how to do that via IO::Pipe, though. Here's what I have:

use strict;
use warnings;

use IO::Pipe;


my($cmd) = join (" ", @ARGV);

open(PIPE, "$cmd |") || die qq(error opening PIPE);
while (<PIPE>) {
        chomp;
        print "DBG1: $_\n";
}

close PIPE;

my($pipe) = IO::Pipe->new();
$pipe->reader($cmd);
die qq(error opening IO::Pipe) if $pipe->eof();

while (<$pipe>) {
        chomp;
        print "DBG2: $_\n";
}

$pipe->close();

If the sub-process command is invalid, both checks will correctly die. If the sub-process produces no output, though, eof() will report an error, even if the command itself is fine:

$ perl pipe.pl "ls -l >/dev/null"
error opening IO::Pipe at pipe.pl line 20.

A bunch of questions, then:

Is there a reasonable OO way to read from a sub-process in Perl? Is IO::Pipe the correct tool to use? If so, how do I check to make sure the sub-process command is created successfully? If not, what should I be using? I don't want to write to the sub-process, so I don't think I want IPC::Open2 or IPC::Open3. I would prefer to use a core module, if possible.

Joe Casadonte
  • 15,888
  • 11
  • 45
  • 57

2 Answers2

3

The issue is not IO::Pipe. The problem is eof is the wrong way to check for a pipe error. It doesn't mean there's no pipe, it means there's nothing to read from that pipe. You'd have the same problem with eof PIPE. It's perfectly fine for a sub-process to not print anything.

If you want to check the sub-process successfully ran, it turns out IO::Pipe already does that for you.

# IO::Pipe: Cannot exec: No such file or directory
$pipe->reader("hajlalglagl");
Schwern
  • 153,029
  • 25
  • 195
  • 336
0

Backticks is not a core module but seems to do what your looking for.

use strict;
use warnings;
use Backticks;
my $cmd = Backticks->new(join (" ", @ARGV));
$cmd->run();

if ($cmd->success){
        print $cmd->stdout
} else {
        print "Command failed\n";
}

Running this with a valid command then invalid command gives the below results

io_pipe.pl "uname -o"

GNU/Linux

io_pipe.pl "uname -z"

Command failed

Update As pointed out by @thisSuitIsNotBlack, this module changes the deafult behaviour of backticks in perl. You should read the Notes section of the POD. However the main one to be aware of is:

The overriding of backticks is provided by Filter::Simple. Source filtering can be weird sometimes... if you want to use this module in a purely traditional Perl OO style, simply turn off the source filtering as soon as you load the module:

 use Backticks;
 no Backticks;
Chris Doyle
  • 10,703
  • 2
  • 23
  • 42
  • 1
    By default, Backticks overrides the behavior of `\`\`` using [source filtering](https://metacpan.org/pod/Backticks#Source-filtering). I would disable this, since [source filters are generally bad](http://stackoverflow.com/questions/1785852/why-are-perl-source-filters-bad-and-when-is-it-ok-to-use-them). The docs recommend `use Backticks; no Backticks;`, although I'm still trying to wrap my head around how that works. – ThisSuitIsBlackNot Nov 10 '15 at 21:24
  • @ThisSuitIsBlackNot - Yeah i noticed that in the POD and various ways to restore the perl backtick functionality but left it for the user to read the POD himself. I assumed if he wanted to use a more OO approach to calling external commands that he would do it for all – Chris Doyle Nov 10 '15 at 21:26
  • My point is that your example code changes the behavior of a core operator, with no indication that it does so. I think that warrants mentioning in the text of your answer (or the code itself) instead of buried in a link. – ThisSuitIsBlackNot Nov 10 '15 at 21:29