Take the 2-minute tour ×

For my upcoming PulseAudio library I want to redirect STDERR and STDOUT to /dev/null logically this works,

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

However, this still outputs to the term....

sub _exec {
    local ( *STDERR, *STDOUT );

That leaves me with two questions

  1. First and foremost, why am I experiencing the behavior that I'm seeing?
  2. Secondly, is there a more efficient method that doesn't involve storing the old value and replacing it?
share|improve this question
might be obvious but have you considered something like CORE::system('pacmd &> /dev/null' ) or die $? –  perreal Dec 12 '12 at 5:47
@perreal there is a reason why I'm not doing that, your statement works because the argument is passed to the shell. I explicitly do not want my command passed to the shell. It seems icky to put a library up on CPAN that uses shell-exec just because I want to cloak stderr and stdout. –  Evan Carroll Dec 12 '12 at 6:00
add comment

1 Answer

up vote 4 down vote accepted

The child writes to fd 1 and 2, yet you didn't change fd 1 and 2. You just created new Perl variables (something the child knows nothing about) with fd 3 and 4 (something the child doesn't care about).

Here's one way of achieving what you want:

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(
        undef,  # 2>&1
        'pacmd', @_,
    waitpid($pid, 0);
    die $! if $? == -1;
    die &? if $?;

open3 is pretty low level, but it's far higher level than doing it yourself*. IPC::Run and IPC::Run3 are even higher level.

* — It takes care for forking and assigning the handles to the right file descriptors. It handles error checking, including making pre-exec errors in the child appear to be the launch failures they are and not errors from the executed program.

share|improve this answer
Ah, so normally STDIN and STDOUT (fd1, fd2) are inherited from the parent process. It's still that way in perl, however in Perl the STDIN and STDOUT symbols have special meaning that don't refer to the underlying architecture's locations? Is that fair to say? –  Evan Carroll Dec 12 '12 at 5:48
I would simply say "storing a file handle in STDIN doesn't magically assign it to fd 0". That's good. Maybe it would be nice if system assign to the child's fd 0 the handle in STDIN, but it doesn't, and it's too late to change that. –  ikegami Dec 12 '12 at 5:51
That clarifies the why perfectly, could you tell just for completeness how you would store a file-handle to fd 0? –  Evan Carroll Dec 12 '12 at 5:53
Amazing, thanks dude. Appreciate it! –  Evan Carroll Dec 12 '12 at 5:53
It works because open *STDIN, '<', ... will reuse the same fd by virtue of being the lowest unopened fd after open closed it. It doesn't work with open local *STDIN, '<', ... because fd 0 is not getting closed. –  ikegami Dec 12 '12 at 6:02
show 2 more comments

Your Answer


