You can start the other program by opening a pipe from it to your Perl program, and then read its output line by line until you reach the terminating condition:
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";
There are a couple of pitfalls to note here, though. One is that closing the pipe will only kill the other program when it next tries to print something. If the other program might run for a long time without generating any output, you may want to kill it explicitly.
For this, you will need to know its process ID, but, conveniently, that's exactly what open
returns when you open a pipe. However, you may want to use the multi-arg version of open
, so that the PID returned will be that of the actual commandA process, rather than of a shell used to launch it:
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;
Another pitfall is output buffering. Most programs don't actually send their output directly to the output stream, but will buffer it until enough has accumulated or until the buffer is explicitly flushed. The reason you don't usually notice this is that, by default, many programs (including Perl) will flush their output buffer at the end of every output line (i.e. whenever a \n
is printed) if they detect that the output stream goes to an interactive terminal (i.e. a tty).
However, when you pipe the output of a program to another program, the I/O libraries used by the first program may notice that the output goes to a pipe rather than to a tty, and may enable more aggressive output buffering. Often this won't be a problem, but in some problematic cases it could add a substantial delay between the time when the other programs prints a string and the time when your program receives it.
Unfortunately, if you can't modify the other program, there's not much you can easily do about this. It is possible to replace the pipe with something called a "pseudo-tty", which looks like an interactive terminal to the other command, but that gets a bit complicated. There's a CPAN module to simplify it a bit, though, called IO::Pty.
(If you can modify the other program, it's a lot easier. For example, if it's another Perl script, you can just add $| = 1;
at the beginning of the script to enable output autoflushing.)