我试图编写一个在另一个进程中运行perl函数并返回一个闭包的辅助函数,该闭包在被调用时一次生成一行输出。
我想出了一种使用pipe
来混合旧文件和新文件的方法。为了使用open(STDOUT, ">&thing")
语法,我在接收器上使用了旧样式,而在源代码中则使用了新样式,因为它需要由闭包捕获,并且我不想给调用者增加负担提供文件句柄。
是否可以在与open(STDOUT, ">&thing")
相同的构造中使用新型文件句柄?
#!/usr/bin/env perl
# pipe.pl
# use pipe() to create a pair of fd's.
# write to one and read from the other.
#
# The source needs to be captured by the closure and can't be
# destructed at the end of get_reader(), so it has to be lexical.
#
# We need to be able to redirect stdout to sink in such a way that
# we actually dup the file descriptor (so shelling out works as intended).
# open(STDOUT, ">&FILEHANDLE") achieves this but appears to require an
# old-style filehandle.
use strict;
use warnings;
sub get_reader {
local *SINK;
my $source;
pipe($source, SINK) or die "can't open pipe!";
my $cpid = fork();
if ($cpid == -1) {
die 'failed to fork';
}
elsif ($cpid == 0) {
open STDOUT, ">&SINK" or die "can't open sink";
system("echo -n hi");
exit;
}
else {
return sub {
my $line = readline($source);
printf "from child (%s)\n", $line;
exit;
}
}
}
sub main {
my $reader = get_reader();
$reader->();
}
main();
运行时会产生
from child (hi)
符合预期。
答案 0 :(得分:4)
sub get_reader {
my ($cmd) = @_;
open(my $pipe, '-|', @$cmd);
return sub {
return undef if !$pipe;
my $line = <$pipe>;
if (!defined($line)) {
close($pipe);
$pipe = undef;
return undef;
}
chomp($line);
return $line;
};
}
如果这还不够好(例如,因为您还需要重定向孩子的STDIN或STDERR),则可以改用IPC :: Run。
use IPC::Run qw( start );
sub get_reader {
my ($cmd) = @_;
my $buf = '';
my $h = start($cmd, '>', \$buf);
return sub {
return undef if !$h;
while (1) {
if ($buf =~ s/^([^\n]*)\n//) {
return $1;
}
if (!$h->pump())) {
$h->finish();
$h = undef;
return substr($buf, 0, length($buf), '') if length($buf);
return undef;
}
}
};
}
无论哪种方式,您现在都可以做
my $i = get_reader(['prog', 'arg', 'arg']);
while (defined( my $line = $i->() )) {
print "$line\n";
}
无论哪种方式,错误处理都留给您。