perl 替换打开的$fh,|- ',... with pipe

ssgvzors  于 8个月前  发布在  Perl
关注(0)|答案(1)|浏览(63)

我的代码目前看起来像这样:

my $fh;
if (...) {
    open $fh, '|-', 'cmd1', 'arg1', ...;
} elsif (...) {
    open $fh, '|-', 'cmd2', 'arg1', ...;
} elsif (...) {
    open $fh, '|-', 'cmd3', 'arg1', ...;
}
while (...) {
    # do stuff
    print $fh $stuff;
}
close $fh;
if ($!) {
    # error handling
}

因此,根据某些条件,我执行一个不同的命令,然后写入相同的内容。我现在想有条件地用管道替换其中一个命令,所以我尝试这样做(为了简洁起见,没有错误处理):

my $fh;
if (...) {
    open $fh, '|-', 'cmd1', 'arg1', ...;
} elsif (...) {
    open $fh, '|-', 'cmd2', 'arg1', ...;
} elsif (...) {
    my $reader, $writer;
    pipe $reader, $writer;
    my $pid = fork();
    if ($pid == 0) {
        open(STDIN, '<&', $fh);
        open(STDOUT, '>&', $writer);
        close($reader);
        exec 'filtercmd';
    }
    close $writer
    open $reader, '|-', 'cmd3', 'arg1', ...;
}
while (...) {
    # do stuff
    print $fh $stuff;
}
close $fh;
if ($!) {
    # error handling
}

目的是生成一个后台进程,我将$fd作为标准输入,然后使用管道写入另一个进程。但是当然这不起作用,因为在那一点上$fh没有定义。当使用open $fh, '|-'时这没有问题,但在这里它是一个问题。在我可以将$fh用作我的forked的stdin之前,我必须对它做些什么吗?我以后想写的东西呢?

编辑

评论建议使用IPC::Open2,而不是自己使用fork()滚动它。我试图改变一个项目,到目前为止没有使用IPC模块,所以我想避免在我的补丁中添加此依赖,但为了论证,让我们尝试使用IPC::Open2创建一个最小的示例。下面的代码应该与echo foo | tee /dev/stderr | tee /dev/stderr类似。我使用tee而不是cat,以便我们可以看到foo在流水线的每一步打印:

my ($reader, $writer, $fh);
pipe $reader, $writer or die "pipe failed: $!";
open2($reader, $fh, 'tee', '/dev/stderr') or die "cannot open2: $!";
open($writer, '|-', 'tee', '/dev/stderr') // die "wpen failed: $!";
print $fh "foo";

这只会打印foo一次,所以它到达了第一个进程,但不会继续传递到管道的下游。

nbewdwxp

nbewdwxp1#

要做到这一点,最简单的方法就是克服你对模块的厌恶,使用IPC::Run。由聪明人编写的模块让你可以简单地做一些相当复杂和/或乏味的事情,这是很好的;他们应该被接受,而不是回避。我们在这里谈论的不是对is-odd的节点级别的无偿依赖。
一个使用它的框架:

#!/usr/bin/env perl
use warnings;
use strict;
use IPC::Run qw/start pump finish/;
use Symbol;

my $h; # The IPC::Run harness returned by start
my $fh = gensym; # IPC::Run can open pipes automatically but needs a valid glob reference first
if (...) {
    # Simple case; a single command that reads anything written to $fh
    $h = start ['cmd1', 'arg1', ...], '<pipe', $fh;
} elsif (...) {
    $h = start ['cmd2', 'arg1', ...], '<pipe', $fh;
} elsif (...) {
    # Complex case; a pipeline of multiple commands
    $h = start ['filtercmd'], '<pipe', $fh, '|', ['cmd3', 'arg1', ...];
}
close $reader;
while (...) {
    # do stuff
    print $fh $stuff;
    pump $h; # Might not be needed when not using strings as input/output sources but let's be safe
}
close $fh;
finish $h;

echo foo | tee /dev/stderr | tee /dev/stderr等价:

#!/usr/bin/env perl
use warnings;
use strict;
use IPC::Run qw/start finish/;
use Symbol;

# Prints foo three times, twice to standard error, once to standard output

my @tee = qw{tee /dev/stderr};
my $fh = gensym;
my $h = start \@tee, '<pipe', $fh, '|', \@tee;
print $fh "foo\n";
close $fh;
finish $h;

相关问题