0
votes

I am using IPC::Open3 for the suggestion given by Hans Lub here.

My issue is that the open3 call works correctly for the first time, but subsequent invocations return the warning:

Use of uninitialized value in numeric ne (!=) at /usr/lib/perl5/5.8.8/IPC/Open3.pm line 215.

The code sample I am using looks like this:

use  IPC::Open3;

my $pid;
# dup the old standard output and error 
open(OLDOUT, ">&STDOUT") or die "Can't dup STDOUT: $!\n";
open(OLDERR, ">&STDERR") or die "Can't dup STDERR: $!\n";

my $transcript_file = "transcript.temp";
# reopen stdout and stderr
open (STDOUT, "|tee -i $transcript_file") or die "Can't reopen STDOUT: $!\n";
open (STDERR, ">&STDOUT")              or die "Can't reopen STDERR: $!\n";

# print statements now write to log
print "Logging important info: blah!\n";
print STDERR "OOPS!\n";

#eval { $pid = open3("\*STDIN", "\*OLDOUT", "\*OLDERR", "ls"); }; # Tried this, but doesnt seem to help. Output does not appear on STDOUT.
eval { $pid = open3(">&STDIN", ">&OLDOUT", ">&OLDERR", "ls"); }; #This works correctly
waitpid( $pid, 0 );

eval { $pid = open3(">&STDIN", ">&OLDOUT", ">&OLDERR", "ls"); }; #First warning
waitpid( $pid, 0 );

eval { $pid = open3(">&STDIN", ">&OLDOUT", ">&OLDERR", "ls"); }; #Second warning
waitpid( $pid, 0 );

I apologize if I look to be trying to get others solve my problems, but I just can't seem to get around this, and looking inside Perl modules is beyond my current understanding.

2
I suggest you start by updating the IPC::Open3 module. In the current version, line 215 is a blank line.Borodin
Try open3(\*STDIN, \*OLDOUT, \*OLDERR).nwellnhof
Unfortunately, updating the module is beyond my sphere of influence. I am forced to use what is available: Perl version 5.8.8, and Open3 V1.1.Abhishek
@nwehhnhof, tried replacing all 3 eval open3's with your suggestion, but now the open3 output does not appear on transcript (intended) as well as STDOUT/STDERR (Unintended).Abhishek
If updating to a new version isn't an option, looking for workarounds might be.Sobrique

2 Answers

2
votes

It doesn't make sense to give the same STDIN to multiple parallel process. open3 thus assumes the handle you tell open3 to use isn't used by anything else, so it closes it.

It looks like your children aren't using the STDIN you provide them, so you should provide a handle to /dev/null.

open(local *CHILD_STDIN, '<', '/dev/null') or die $!;
$pid = open3('<&CHILD_STDIN', '>&STDOUT', '>&STDERR', @cmd);
1
votes

I think the problem is the way open3 uses the file handles that you pass. If you use, say, >&STDOUT then the file handle is duped, the dupe is passed to the child process, and the parent's copy is closed. That means the second time you do the same thing you are duping a closed file handle, which doesn't have the effect you want.

The only way around this that I can see is to dupe the file handles separately and pass the dupes to the child process. It won't matter that the parent's copy of the dupes is closed because it still has the original STDOUT etc. Unfortunately it adds another three statements to each open3 call, so you woul probably want to wrap the whole thing in a subroutine, like this.

my_open3('ls');
my_open3('ls');
my_open3('ls');

sub my_open3 {

  my @cmd = @_;
  my $pid;

  open IN_COPY,  '<&', STDIN  or die "Couldn't dup STDIN: $!";
  open OUT_COPY, '>&', STDOUT or die "Couldn't dup STDOUT: $!";
  open ERR_COPY, '>&', STDERR or die "Couldn't dup STDERR: $!";

  eval {
    $pid = open3('>&IN_COPY', '>&OUT_COPY', '>&ERR_COPY', @cmd);
  };

  waitpid $pid, 0;
}

This isn't the nicest of solutions, so if anyone can see anything better then please chime in. The only alternative I can see is to let the parent keep its own standard IO handles and use completely new ones to communicate with the child process each time. Then the parent would have mess with IO::Select to do the copying from the child output to its own STDOUT and STDERR.

As nwellnhof says, if the child doesn't use its STDIN (as is the case with the ls command) then you can just pass undef as the first parameter. That saves duplicating one of three standard handles.