您可以通过从另一个程序open
到您的 Perl 程序的管道来启动另一个程序,然后逐行读取它的输出,直到达到终止条件:
open my $pipe, 'commandA |'
or die "Error opening pipe from commandA: $!\n";
my $n = 0;
while (<$pipe>) {
$n++ if /banana/;
last if $n >= 10;
}
close $pipe; # kills the command with SIGPIPE if it's not done yet
print "commandA printed 'banana' ", ($n >= 10 ? "at least 10" : $n), " times.\n";
不过,这里有几个陷阱需要注意。一个是关闭管道只会在下一次尝试打印某些内容时杀死另一个程序。如果其他程序可能运行很长时间而不产生任何输出,您可能需要kill
明确地执行它。
为此,您需要知道它的进程 ID,但方便的是,这正是open
您打开管道时返回的内容。但是,您可能希望使用 的多参数版本open
,以便返回的 PID 将是实际 commandA 进程的 PID,而不是用于启动它的 shell:
my $pid = open my $pipe, '-|', 'commandA', @args
or die "Error opening pipe from commandA: $!\n";
# ...
kill 'INT', $pid; # make sure the process dies
close $pipe;
另一个陷阱是输出缓冲。大多数程序实际上并不直接将它们的输出发送到输出流,而是将其缓冲直到积累足够多或直到缓冲区被显式刷新。您通常不会注意到这一点的原因是,默认情况下,如果许多程序(包括 Perl)\n
检测到输出流转到交互式终端(即 tty)。
但是,当您将一个程序的输出通过管道传输到另一个程序时,第一个程序使用的 I/O 库可能会注意到输出进入管道而不是 tty,并且可能会启用更积极的输出缓冲。通常这不会成为问题,但在某些有问题的情况下,它可能会在其他程序打印字符串的时间与您的程序接收它的时间之间增加相当大的延迟。
不幸的是,如果您无法修改其他程序,那么您将无法轻松做到这一点。可以用一种叫做“pseudo-tty”的东西来替换管道,它看起来像另一个命令的交互式终端,但这有点复杂。不过,有一个 CPAN 模块可以稍微简化它,称为IO::Pty。
(如果你可以修改其他程序,那就容易多了。例如,如果它是另一个 Perl 脚本,你可以$| = 1;
在脚本的开头添加以启用输出自动刷新。)