代码之家  ›  专栏  ›  技术社区  ›  Greg Nisbet

Perl重定向STDUT到词汇文件句柄

  •  1
  • Greg Nisbet  · 技术社区  · 6 年前

    我试图编写一个helper函数,它在另一个进程中运行一个perl函数,并返回一个闭包,该闭包在调用时一次生成一行输出。

    我想出了一个办法 pipe 混合了新旧风格的文件句柄。我在水槽上用了一个老式的,以便 open(STDOUT, ">&thing") 语法和源代码的新样式,因为它需要由闭包捕获,我不想让调用方负担提供文件句柄的负担。

    在具有与 打开(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 回复  |  直到 6 年前
        1
  •  4
  •   ikegami    6 年前
    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";
    }
    

    不管怎样,错误处理留给你。