两次使用open3时IPC :: Open3中的警告

时间:2015-03-16 11:08:34

标签: perl ipcopen3

我正在使用IPC::Open3来提出Hans Lub提出的建议here

我的问题是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模块是我目前无法理解的。

2 个答案:

答案 0 :(得分:2)

将相同的STDIN赋予多个并行进程是没有意义的。因此,open3假设您告诉open3使用的句柄未被其他任何内容使用,因此会将其关闭。

看起来您的孩子没有使用您提供的STDIN,因此您应该提供/dev/null的句柄。

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

答案 1 :(得分:1)

我认为问题是open3使用您传递的文件句柄的方式。如果你使用>&STDOUT,那么文件句柄被欺骗,欺骗被传递给子进程,而父进程的副本关闭。这意味着你第二次做同样的事情就是复制一个关闭的文件句柄,它没有你想要的效果。

我能看到的唯一方法是分别对文件句柄进行欺骗并将欺骗传递给子进程。父亲的欺骗副本被关闭是非常重要的,因为它仍然有原始的STDOUT等。不幸的是,它为每个open3电话添加了另外三个语句,所以你woul可能想把整个事情包装在子程序中,就像这样。

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作为第一个参数。这样可以节省三个标准句柄中的一个重复。