【问题标题】:Why doesn't local work on STDERR and STDOUT?为什么本地不适用于 STDERR 和 STDOUT?
【发布时间】:2012-11-29 18:19:17
【问题描述】:

对于我即将推出的 PulseAudio 库,我想在逻辑上将 STDERRSTDOUT 重定向到 /dev/null,这是可行的,

sub _exec {
    open (*STDERR, '>', '/dev/null');    
    open (*STDOUT, '>', '/dev/null');    
    CORE::system('pacmd', @_ ) or die $?;

但是,这仍然输出到术语....

sub _exec {
    local ( *STDERR, *STDOUT );
    open (*STDERR, '>', '/dev/null');    
    open (*STDOUT, '>', '/dev/null');    
    CORE::system('pacmd', @_ ) or die $?;

这让我有两个问题

  1. 首先,为什么我会遇到我所看到的行为?
  2. 其次,有没有更有效的方法,不涉及存储旧值并替换它?

【问题讨论】:

  • 可能很明显,但您是否考虑过类似CORE::system('pacmd &> /dev/null' ) or die $?
  • @perreal 我不这样做是有原因的,您的陈述有效,因为参数已传递给外壳。我明确不希望我的命令传递给 shell。仅仅因为我想隐藏 stderr 和 stdout 就在 CPAN 上放置一个使用 shell-exec 的库似乎很恶心。

标签: perl redirect stdout stderr


【解决方案1】:

孩子写入 fd 1 和 2,但您没有更改 fd 1 和 2。您只是使用 fd 3 和 4(孩子不关心的东西)创建了新的 Perl 变量(孩子一无所知)大约)。

这是实现您想要的一种方法:

use IPC::Open3 qw( open3 );

sub _exec {
    open(local *CHILD_STDIN,  '<', '/dev/null') or die $!;
    open(local *CHILD_STDOUT, '>', '/dev/null') or die $!;
    my $pid = open3(
        '<&CHILD_STDIN',
        '>&CHILD_STDOUT',
        undef,  # 2>&1
        'pacmd', @_,
    );
    waitpid($pid, 0);
    die $! if $? == -1;
    die &? if $?;
}

open3 级别很低,但比自己做的级别要高得多*。 IPC::RunIPC::Run3 级别更高。



* - 它负责将句柄分叉并分配给正确的文件描述符。它处理错误检查,包括使子进程中的 pre-exec 错误看起来是启动失败,而不是来自已执行程序的错误。

【讨论】:

  • 啊,所以通常STDINSTDOUT (fd1, fd2) 是从父进程继承的。在 perl 中仍然是这样,但是在 Perl 中,STDINSTDOUT 符号具有特殊含义,它们不涉及底层架构的位置?这样说公平吗?
  • 我只想说“在 STDIN 中存储文件句柄不会神奇地将其分配给 fd 0”。那挺好的。如果system 将 STDIN 中的句柄分配给孩子的 fd 0 可能会很好,但它没有,现在改变它为时已晚。
  • 完美地阐明了为什么,您能否为了完整起见告诉您如何将文件句柄存储到fd 0
  • 其实还有一个问题@ikegami,没有本地怎么办?没有本地 perl 怎么会将这些文件句柄写入逻辑 fd?为什么local() 下的行为不同,应该是perldoc -f local
  • 它之所以有效,是因为open *STDIN, '&lt;', ...open 关闭它之后,由于是最低的未打开 fd,因此将重用相同的 fd。它不适用于open local *STDIN, '&lt;', ...,因为 fd 0 没有关闭。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-04-23
  • 2011-10-30
  • 1970-01-01
  • 1970-01-01
  • 2020-01-11
  • 1970-01-01
  • 2020-05-22
相关资源
最近更新 更多