HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

The right way to use IPC::Open3 in Perl

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
theperlopen3wayipcuseright

Problem

Here is the function which does exactly what is specified by its name:

sub runCmd_getStdoutStderrAndExitCode(@)
{
  my @cmd = @_;

  # fork a child
  my ($child_pid, $child_stdout, $child_stderr);
  eval
  {
    $child_pid = open3(\*STDIN, # is /dev/null when collectl is a daemon, UPDATE: can pass 'undef' here
                       $child_stdout,
                       $child_stderr,
                       @cmd);
  };
  if ($@ =~ /^open3:/)
  {
    syslog(LOG_ERR, 'Error in "%s" : %s', "@cmd", $@);
    return (undef, undef, -1);
  }

   # result buffers
  my $stdout = '';
  my $stderr = '';

  # read into result buffers avoid blocking
  my $selector = IO::Select->new();
  $selector->add($child_stdout, $child_stderr);
  while (my @ready = $selector->can_read(SOME_TIMEOUT_CONSTANT))
  {
    foreach my $fh (@ready) {
      my $target_buf = ($fh == $child_stdout)
                     ? \$stdout
                         : \$stderr;
      if (!sysread($fh, $target_buf, 4096, length($target_buf)))
      {
        $selector->remove($fh);
        close $fh;
      }
    }
  }
  close $_ for ($selector->handles());

  my $finished = waitpid($child_pid, WNOHANG);
  return ($stdout, $stderr, $?) if $finished;

  if (kill 'TERM' => $child_pid) {
    # hang until SIGTERM is processed then bury child process
    waitpid($child_pid, 0);
  } else {
    syslog(LOG_WARNING, 'Error sending TERM to child process "%s"', "@cmd");
  }
  return ($stdout, $stderr, -1);
}


After the script successfully completes its job and before Perl interpreter exits, I get this message in the terminal:


stty: standard input: Bad file descriptor

It would be nice to have your opinion about the code correctness, and get a piece of advice about how to avoid that message, as a bonus.

UPDATE: The message mentioned gone away when I replaced '\*STDIN' arg with 'undef'.

Major update: It appears that this code is not functioning properly. It mixes stderr and stdout from child process, and returns e

Solution

as it say in the IPC::Open3 documentation, the filehandle for STDERR cannot be autovivified. You need to

use Symbol qw/ gensym /; my $stderr = gensym;


to create a file handle before opening your command.

Code Snippets

use Symbol qw/ gensym /; my $stderr = gensym;

Context

StackExchange Code Review Q#84496, answer score: 5

Revisions (0)

No revisions yet.