Используйте IPC::Open3 с perlcritic

Я хочу подавить вывод в дочернем процессе и только для чтения stderr. Perlfaq8 советует сделать следующее:

# To capture a program's STDERR, but discard its STDOUT:
use IPC::Open3;
use File::Spec;
use Symbol qw(gensym);
open(NULL, ">", File::Spec->devnull);
my $pid = open3(gensym, ">&NULL", \*PH, "cmd");
while( <PH> ) { }
waitpid($pid, 0);

Но потом perlcritic утверждает использование файловых дескрипторов без слов.

Единственное, что я могу придумать, это select вновь открытый дескриптор /dev/null вместо STDOUT, как это:

# To capture a program's STDERR, but discard its STDOUT:
use IPC::Open3;
use File::Spec;
use Symbol qw(gensym);
open my $null, ">", File::Spec->devnull;
my $old_stdout = select( $null );
my $pid = open3(gensym, ">&STDOUT", \*PH, "cmd");
select( $old_stdout );
while( <PH> ) { }
waitpid($pid, 0);

Но потом perlcritic не любит использованиеselect, Есть ли более элегантное решение?

3 ответа

Решение

Минимальное изменение - просто сделать использование NULL в open больше не голым словом, изменив его на *NULL.

Обычно все еще считается плохой формой использовать дескрипторы этой формы (поскольку они являются глобальными переменными, хотя вы можете сделать их несколько менее глобальными, применив к ним локальные переменные) Поэтому я бы рекомендовал изменить его, чтобы вместо этого использовать мои переменные для всех дескрипторов. Выглядит так, как будто вы отбрасываете дескриптор файла stdin, так что тоже можно передать нулевой дескриптор файла (обратите внимание, я открываю его в режиме чтения-записи)

use strict;
use warnings;

use IPC::Open3;
use File::Spec;
use Symbol qw(gensym);

open(my $null, '+>', File::Spec->devnull);

my $childErr = gensym;

my $pid = open3($null, $null, $childErr, "cmd");

while(<$childErr>) { }
waitpid($pid, 0);
  • Ваш select на самом деле ничего не делает! select не меняется STDOUT,
  • Передача дескриптора закрытого файла в STDIN программы может вызвать проблемы.

Fix:

use File::Spec qw( );
use IPC::Open3 qw( open3 );

my $child_stderr;
my $pid = do {
   open(local *CHILD_STDIN,  '<', File::Spec->devnull) or die $!;
   open(local *CHILD_STDOUT, '>', File::Spec->devnull) or die $!;
   $child_stderr = \( local *CHILD_STDERR );
   open3('<&CHILD_STDIN', '>&CHILD_STDOUT', $child_stderr, $cmd)
};
while (<$child_stderr>) { }
waitpid($pid, 0);

Заметки:

  1. Я не использую дескрипторы открытых файлов open3 кроме как через '<&SYM' а также '>&SYM' механизм. Есть по крайней мере одно место, где есть проблема, если нет.

  2. Существуют модули более высокого уровня, которые проще в использовании, такие как IPC:: Run3 и IPC:: Run.

  3. С помощью File::Spec->devnull() вместо '/dev/null' вероятно, излишним. Будет ли ваша программа работать на других платформах без /dev/null?

>&... Выражение также может включать числовой дескриптор файла, поэтому

open my $NULL, '>', ... ;
my $pid = open3(gensym, ">&" . fileno($NULL), \*PH, "cmd");

является лексическим эквивалентом дескриптора файла

open NULL, '>', ... ;
my $pid = open3(gensym, ">&NULL", \*PH, "cmd");
Другие вопросы по тегам