【问题标题】:Use IPC::Open3 with perlcritic将 IPC::Open3 与 perlcritic 一起使用
【发布时间】:2013-12-22 00:26:48
【问题描述】:

我想抑制子进程中的输出并只读 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);

但随后perlcriticusing bareword file handles 争论。

我唯一能想到的是将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 不喜欢using of select。 有没有更优雅的解决方案?

【问题讨论】:

  • 您的select 实际上并没有做任何事情! select 不会改变 STDOUT
  • perlcritic 经常是错误的。

标签: perl perl-critic ipcopen3


【解决方案1】:

最小的改变就是通过将 NULL 更改为 *NULL 来使 open 中的 NULL 不再是一个裸词。

使用这种形式的句柄通常仍然被认为是糟糕的形式(因为它们是全局变量,尽管您可以通过对它们应用局部来使它们的全局性降低一些)。因此,我建议将其更改为将我的变量用于所有句柄。看起来您正在丢弃标准输入文件句柄,因此也可以传递空文件句柄(注意我以读写模式打开它)

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

【讨论】:

  • 关于“使用这种形式的句柄通常仍然被认为是糟糕的形式”,除了 IPC::Open3 是一个围绕它们的使用而设计的旧模块并且不能很好地与词法文件句柄一起使用。
  • 非常正确,我很确定在过去的 10 年里我从来没有使用过 gensym。另外,我的 open 应该使用 '+
【解决方案2】:
  • 您的select 实际上并没有做任何事情! select 不会改变 STDOUT
  • 将关闭的文件句柄传递给程序的 STDIN 可能会导致问题。

修复:

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,除非通过'&lt;&amp;SYM''&gt;&amp;SYM' 机制。如果你不这样做,至少有一个地方会出现问题。

  2. 还有更高级的模块,例如IPC::Run3IPC::Run

  3. 使用File::Spec-&gt;devnull() 而不是'/dev/null' 可能是矫枉过正。如果没有/dev/null,你的程序真的可以在其他平台上运行吗?

【讨论】:

  • perlcritic 仍在争论使用 typeglob,它认为 STDIN、STDOUT 和 STDERR 是唯一允许的裸字文件句柄。 IPC::Run3IPC::Run 都接受文件句柄,所以它解决了问题,谢谢。我无法避免使用devnull - 这个小实用脚本必须在 Windows 和 *nix 上都使用,Win 使用 NUL 而不是 /dev/null
  • @darkmist,当然可以。你不应该使用 glob。但是您必须使用open3。我很失望你接受了我特别说你应该避免的问题的答案。
  • (顺便说一句,\( local *CHILD_STDERR )gensym 是同一个东西。随意使用您选择的一个。我只是没有看到加载模块的意义。)跨度>
  • 回复:“啊,所以你在使用 Win32::ShellQuote 和 String::ShellQuote 来构建 $cmd 之间动态切换?”我为什么要这样做? open3 分别接受 cmd 和 args,就像 system 一样。
【解决方案3】:

&gt;&amp;... 表达式还可以包含数字文件描述符,所以

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

是词法文件句柄等价于

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

【讨论】:

    猜你喜欢
    • 2014-07-09
    • 2017-02-18
    • 2012-08-17
    • 2013-07-16
    • 2017-04-20
    • 1970-01-01
    • 2012-04-19
    • 1970-01-01
    • 2011-01-07
    相关资源
    最近更新 更多