2014-09-01 67 views
1

我正在嘗試將分叉進程的STDOUT分配給管道。例如爲分叉進程重新分配STDOUT

use warnings; 
use strict; 
use feature qw(say); 
use IO::Select; 

my $cmd='sleep 2; echo Hello'; 
pipe(PREAD, PWRITE); 

my $pid=fork(); 
if ($pid==0) { 
    *STDOUT=*PWRITE; 
    exec $cmd; 
    die "Could not run command \"$cmd\""; 
} 
my $sel = IO::Select->new(\*PREAD); 
say "Waiting for background process.."; 
while (1) { 
    my @ready = $sel->can_read; 
    last if (! @ready); 
    for my $fh (@ready) { 
    say "Processing file descriptor ".($fh->fileno()); 
    my $line=<$fh>; 
    if (! defined $line) { 
     say "EOF"; 
     $sel->remove($fh); 
     next; 
    } 
    chomp($line); 
    say "Got line: \"$line\".."; 
    } 
} 
waitpid ($pid,0); 
say "Done."; 

但是沒有任何內容出現在管道中PREAD;孩子仍然輸出到STDOUT而不是管道。

+2

perl有一個快捷方式:'打開我的$ pipe,'$ cmd |''或'打開我的$ pipe,' - ',$ cmd,@ args'。請參閱[文檔](https://metacpan.org/pod/distribution/perl/pod/perlfunc.pod#open-FILEHANDLE-EXPR)。 – salva 2014-09-01 07:01:04

回答

2

* STDOUT = * PWRITE;

這隻會將perl文件句柄PWRITE分配給perl文件句柄STDOUT,但不會更改操作系統的底層文件描述符。爲此,您需要將基礎文件描述符從PWRITE複製到fd 1(STDOUT):

open(STDOUT,「> & PWRITE」);

完成此操作後,PWRITE將被複制,現在可作爲PWRITE和STDOUT訪問。一個好的風格是close(PWRITE)然後使STDOUT成爲寫入管道的唯一文件句柄。

+0

@hobbs:你是對的,我已經相應地改變了它。 – 2014-09-01 06:16:48

+0

謝謝Steffen,這工作..但是現在'選擇'循環沒有結束..我怎麼能檢測'select'循環中的EOF? – 2014-09-01 06:27:49

+2

直到PWRITE關閉,PREAD纔會返回EOF。在'exec'完成後,它在孩子內部被關閉,但你仍然在父進程中保持打開狀態。所以你需要在fork之後的父級中添加一個明確的「close(PWRITE)」。 – 2014-09-01 06:47:50