我的代码目前看起来像这样:
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
一次,所以它到达了第一个进程,但不会继续传递到管道的下游。
1条答案
按热度按时间nbewdwxp1#
要做到这一点,最简单的方法就是克服你对模块的厌恶,使用
IPC::Run
。由聪明人编写的模块让你可以简单地做一些相当复杂和/或乏味的事情,这是很好的;他们应该被接受,而不是回避。我们在这里谈论的不是对is-odd
的节点级别的无偿依赖。一个使用它的框架:
和
echo foo | tee /dev/stderr | tee /dev/stderr
等价: