Perl将stdout重定向到词法文件句柄

时间:2018-07-14 00:20:45

标签: perl

我试图编写一个在另一个进程中运行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)

符合预期。

1 个答案:

答案 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";
}

无论哪种方式,错误处理都留给您。