您可以通过以下方式启动其他程序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 的 PID:
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
已打印)if它们检测到输出流进入交互式终端(即 tty)。
但是,当您将一个程序的输出通过管道传输到另一个程序时,第一个程序使用的 I/O 库可能会注意到输出进入管道而不是 tty,并且可能启用更积极的输出缓冲。通常这不会成为问题,但在某些有问题的情况下,它可能会在其他程序打印字符串的时间与您的程序接收字符串的时间之间增加相当大的延迟。
不幸的是,如果您无法修改其他程序,则对此您无能为力。它is可以用称为“伪tty”的东西替换管道,它看起来像其他命令的交互式终端,但这有点复杂。不过,有一个 CPAN 模块可以稍微简化它,称为IO::Pty.
(If you can修改其他程序,就容易多了。例如,如果它是另一个 Perl 脚本,您只需添加$| = 1;
在脚本的开头以启用输出自动刷新。)