Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't local work on STDERR and STDOUT?

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 );
    open (*STDERR, '>', '/dev/null');    
    open (*STDOUT, '>', '/dev/null');    
    CORE::system('pacmd', @_ ) or die $?;

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?
like image 374
NO WAR WITH RUSSIA Avatar asked Dec 12 '12 05:12

NO WAR WITH RUSSIA


1 Answers

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(
        '<&CHILD_STDIN',
        '>&CHILD_STDOUT',
        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.

like image 99
ikegami Avatar answered Sep 27 '22 22:09

ikegami