Предупреждение в IPC::Open3 при использовании open3 дважды
Я использую IPC:: Open3 для предложения Ханса Луба.
Моя проблема в том, что 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 ответа
Не имеет смысла давать один и тот же STDIN нескольким параллельным процессам. open3
таким образом предполагает ручку, которую вы говорите open3
использование не используется ничем другим, поэтому оно закрывает его.
Похоже, что ваши дети не используют STDIN, который вы им предоставляете, поэтому вы должны предоставить ручку /dev/null
,
open(local *CHILD_STDIN, '<', '/dev/null') or die $!;
$pid = open3('<&CHILD_STDIN', '>&STDOUT', '>&STDERR', @cmd);
Я думаю, что проблема в том, open3
использует файловые дескрипторы, которые вы передаете. Если вы используете, скажем, >&STDOUT
затем дескриптор файла дублируется, dupe передается дочернему процессу, а родительская копия закрывается. Это означает, что во второй раз, когда вы делаете то же самое, вы дублируете дескриптор закрытого файла, что не дает желаемого эффекта.
Единственный способ обойти это, как я вижу, состоит в том, чтобы дублировать файловые дескрипторы отдельно и передавать дуплексы дочернему процессу. Неважно, что родительская копия dupes закрыта, потому что она все еще имеет оригинал 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::Select
сделать копирование с дочернего вывода на свой собственный STDOUT
а также STDERR
,
Как nwellnhof
говорит, что если ребенок не использует его STDIN
(как в случае с ls
команда), то вы можете просто передать undef
в качестве первого параметра. Это экономит дублирование одной из трех стандартных ручек.