Yet another way to launch a process is to create a process that looks like a filehandle (similar to the popen (3) C library routine if you're familiar with that). We can create a process-filehandle that either captures the output from or provides input to the process.[4] Here's an example of creating a filehandle out of a who(1) process. Because the process is generating output that we want to read, we make a filehandle that is open for reading, like so:
open(WHOPROC, "who|"); # open who for reading
[4] But not both at once. See Chapter 6 of Programming Perl or perlipc (1) for examples of bidirectional communication.
Note the vertical bar on the right side of who
. That bar tells Perl that this open
is not about a filename, but rather a command to be started. Because the bar is on the right of the command, the filehandle is opened for reading, meaning that the standard output of who is going to be captured. (The standard input and standard error remain shared with the Perl process.) To the rest of the program, the WHOPROC
handle is merely a filehandle that is open for reading, meaning that all normal file I/O operators apply. Here's a way to read data from the who command into an array:
@whosaid = <WHOPROC>;
Similarly, to open a command that expects input, we can open a process-filehandle for writing by putting the vertical bar on the left of the command, like so:
open(LPR,"|lpr -Pslatewriter"); print LPR @rockreport; close(LPR);
In this case, after opening LPR
, we write some data to it and then close it. Opening a process with a process-filehandle allows the command to execute in parallel with the Perl program. Saying close
on the filehandle forces the Perl program to wait until the process exits. If you don't close the filehandle, the process can continue to run even beyond the execution of the Perl program.
Opening a process for writing causes the command's standard input to come from the filehandle. The process shares the standard output and standard error with Perl. As before, you may use /bin/sh-style I/O redirection, so here's one way to simply discard the error messages from the lpr command in that last example:
open(LPR,"|lpr -Pslatewriter >/dev/null 2>&1");
The >/dev/null
causes standard output to be discarded by being redirected to the null device. The 2>&1
causes standard error to be sent to where the standard output is sent, resulting in errors being discarded as well.
You could even combine all this, generating a report of everyone except Fred in the list of logged-on entries, like so:
open (WHO,"who|"); open (LPR,"|lpr -Pslatewriter"); while (<WHO>) { unless (/fred/) { # don't show fred print LPR $_; } } close WHO; close LPR;
As this code fragment reads from the WHO
handle one line at a time, it prints all of the lines that don't contain the string fred
to the LPR
handle. So the only output on the printer is the lines that don't contain fred
.
You don't have to open just one command at a time. You can open an entire pipeline. For example, the following line starts up an ls (1) process, which pipes its output into a tail (1) process, which finally sends its output along to the WHOPR
filehandle:
open(WHOPR, "ls | tail -r |");