Want to improve this question? Update the question so it's on-topic for Stack Overflow.
Closed 6 years ago.
I have some code running. Because of the complexity and the length I thought maybe use some code to make my life easy. So the code is running using >commandA
output
results
are
popping
...
here
I want to count the number of times banana
appears in the output of commandA
(which is running) and when the count is 10, I want to stop the processing (using CTRL+Z) and
echo "************we reached 10**********************"
and start again.
I am writing the code in perl on unix system.
EDIT: I cannot use grep function here as the command has already run. Or will be run but without a grep function. Before the command runs, I will turn on my program to look for the specific words in the terminal output. Now it would very easy to use grep, but I don't know which function in perl actually takes in the output to the terminal as stdin
You can start the other program by open
ing 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.)