在多个函数之间将 stdout 管道传输到 perl 中的 stdin(例如 shell 中的 cmd1 | cmd2 | cmd3)
Piping stdout to stdin in perl between multiple functions (like cmd1 | cmd2 | cmd3 in shell)
我需要处理大量数据流。流大于将适合 RAM。
流需要按顺序应用多个转换。相互通信的异步执行任务的管道会很好地工作,很像下面的 shell 管道:
producer | transform1 | transform1 | transform2 | consumer
其中一些转换最好通过命令行实用程序实现,这些实用程序接受来自 STDIN 的输入并将其输出生成到 STDOUT。
但是其中一些转换很容易用 Perl 编写。我想避免必须管理许多小脚本,所以我希望它们由启动管道的程序中的 Perl subs 处理。这些 sub 需要在子进程中 运行,从 STDIN 读取并写入 STDOUT,就像此管道中的其他任务一样。这意味着我不能只使用 system
.
如何在 Perl 中实现此管道?
# This is the function to do the work
sub pipefunc {
my $func = pop;
my $pid = open(my $kid_to_read, "-|");
defined($pid) || die "can't fork: $!";
if ($pid) {
open STDIN, "<&", $kid_to_read or die;
&$func();
} else { # child
close $kid_to_read;
if($_[1]) {
# More than one function remaining: Recurse
pipefunc(@_);
} else {
# Only one function remaining: Run it
$func = pop;
&$func();
}
exit 0;
}
}
sub func1 {
# Run some UNIX filter, that reads STDIN and prints to STDOUT
while(<STDIN>) {
print "foo $_";
}
}
sub func2 {
# Run some UNIX filter, that reads STDIN and prints to STDOUT
while (<STDIN>) {
print "bar $_";
}
}
sub func3 {
# Run some UNIX filter, that reads STDIN and prints to STDOUT
exec q{perl -pe 's/o/e/g; s/a/i/g;'};
}
sub func4 {
# Run some UNIX filter, that reads STDIN and prints to STDOUT
exec q{perl -pe 's/^/A/g; s/$/B/g;'};
}
# This is how you use it.
# func4 | func2 | func1 | func3 | func2 | func4
pipefunc(\&func4,\&func2,\&func1,\&func3,\&func2,\&func4);
尝试:
seq 10 | perl pipefunc.pl
就像 shell 中的管道一样,它可以在大于系统虚拟内存的输入上正常工作。就像 shell 中的管道一样,它将 运行 每个函数的进程。
使用 system
很容易将 perl 函数与 shell 命令混合使用,因此通过将 shell 命令包装在 sub myfunc { system "shell command"; }
中,您可以获得以下效果:perlfunc | shell command | perlfunc | perlfunc | shell command
.
让 myfunc1
、myfunc2
和 myfunc3
接受输入作为子程序的参数,return 将输出作为子程序的 return 值。即改
sub myfunc1 {
while (<STDIN>) {
do something
print STDOUT $_;
}
}
类似于
sub myfunc1 {
return map {
do something;
$_
} @_;
}
然后你可以像这样链接你的函数:
print STDOUT myfunc3(myfunc2(myfunc1(<STDIN>)));
IPC::Run
软件包将为您完成此操作。
#! /usr/bin/perl
use IPC::Run qw(run);
my @cat = qw(cat);
my @wc = qw(wc -l);
run @cat, '|', \@wc;
我需要处理大量数据流。流大于将适合 RAM。
流需要按顺序应用多个转换。相互通信的异步执行任务的管道会很好地工作,很像下面的 shell 管道:
producer | transform1 | transform1 | transform2 | consumer
其中一些转换最好通过命令行实用程序实现,这些实用程序接受来自 STDIN 的输入并将其输出生成到 STDOUT。
但是其中一些转换很容易用 Perl 编写。我想避免必须管理许多小脚本,所以我希望它们由启动管道的程序中的 Perl subs 处理。这些 sub 需要在子进程中 运行,从 STDIN 读取并写入 STDOUT,就像此管道中的其他任务一样。这意味着我不能只使用 system
.
如何在 Perl 中实现此管道?
# This is the function to do the work
sub pipefunc {
my $func = pop;
my $pid = open(my $kid_to_read, "-|");
defined($pid) || die "can't fork: $!";
if ($pid) {
open STDIN, "<&", $kid_to_read or die;
&$func();
} else { # child
close $kid_to_read;
if($_[1]) {
# More than one function remaining: Recurse
pipefunc(@_);
} else {
# Only one function remaining: Run it
$func = pop;
&$func();
}
exit 0;
}
}
sub func1 {
# Run some UNIX filter, that reads STDIN and prints to STDOUT
while(<STDIN>) {
print "foo $_";
}
}
sub func2 {
# Run some UNIX filter, that reads STDIN and prints to STDOUT
while (<STDIN>) {
print "bar $_";
}
}
sub func3 {
# Run some UNIX filter, that reads STDIN and prints to STDOUT
exec q{perl -pe 's/o/e/g; s/a/i/g;'};
}
sub func4 {
# Run some UNIX filter, that reads STDIN and prints to STDOUT
exec q{perl -pe 's/^/A/g; s/$/B/g;'};
}
# This is how you use it.
# func4 | func2 | func1 | func3 | func2 | func4
pipefunc(\&func4,\&func2,\&func1,\&func3,\&func2,\&func4);
尝试:
seq 10 | perl pipefunc.pl
就像 shell 中的管道一样,它可以在大于系统虚拟内存的输入上正常工作。就像 shell 中的管道一样,它将 运行 每个函数的进程。
使用 system
很容易将 perl 函数与 shell 命令混合使用,因此通过将 shell 命令包装在 sub myfunc { system "shell command"; }
中,您可以获得以下效果:perlfunc | shell command | perlfunc | perlfunc | shell command
.
让 myfunc1
、myfunc2
和 myfunc3
接受输入作为子程序的参数,return 将输出作为子程序的 return 值。即改
sub myfunc1 {
while (<STDIN>) {
do something
print STDOUT $_;
}
}
类似于
sub myfunc1 {
return map {
do something;
$_
} @_;
}
然后你可以像这样链接你的函数:
print STDOUT myfunc3(myfunc2(myfunc1(<STDIN>)));
IPC::Run
软件包将为您完成此操作。
#! /usr/bin/perl
use IPC::Run qw(run);
my @cat = qw(cat);
my @wc = qw(wc -l);
run @cat, '|', \@wc;