2014-09-01 49 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