0

我正在使用IPC::Open3来获取 Hans Lub在这里给出的建议。

我的问题是open3调用第一次正常工作,但随后的调用返回警告:

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

我使用的代码示例如下所示:

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 );

如果我想让其他人解决我的问题,我深表歉意,但我似乎无法解决这个问题,并且查看 Perl 模块超出了我目前的理解范围。

4

2 回答 2

2

为多个并行进程提供相同的 STDIN 是没有意义的。open3因此假设您告诉open3使用的句柄没有被其他任何东西使用,所以它关闭它。

看起来您的孩子没有使用您提供给他们的 STDIN,因此您应该提供/dev/null.

open(local *CHILD_STDIN, '<', '/dev/null') or die $!;
$pid = open3('<&CHILD_STDIN', '>&STDOUT', '>&STDERR', @cmd);
于 2015-03-16T18:23:21.757 回答
1

我认为问题在于open3使用您传递的文件句柄的方式。如果你使用,比如说,>&STDOUT文件句柄被复制,复制被传递给子进程,并且父进程的副本被关闭。这意味着您第二次做同样的事情时,您正在复制一个关闭的文件句柄,这没有您想要的效果。

我能看到的唯一解决方法是分别复制文件句柄并将复制的内容传递给子进程。关闭父母的欺骗副本并不重要,因为它仍然具有原始STDOUT等。不幸的是,它会在每次调用中添加另外三个语句open3,因此您可能希望将整个内容包装在一个子例程中,就像这样。

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;
}

这不是最好的解决方案,所以如果有人能看到更好的东西,请加入。我能看到的唯一选择是让父进程保留自己的标准 IO 句柄并使用全新的句柄与子进程进行通信时间。然后父母会搞砸IO::Select从孩子输出复制到它自己的STDOUTSTDERR

如前所述nwellnhof,如果孩子不使用它STDIN(如ls命令的情况),那么您可以将undef其作为第一个参数传递。这样可以避免复制三个标准手柄之一。

于 2015-03-16T11:57:18.607 回答