Perl pipeline FAQ: How can I read output from a shell command pipeline (pipe) in a Perl script?
One of the great things about Perl is that it's very easy to run operating system commands, and read the output of those commands. Perl makes this process very easy and natural - it's just like reading data from a file. In this article we'll demonstrate the process of running external commands from within Perl, and then reading the output of those commands.
Perl shell command pipeline example - reading the date
Here's a simple example of running a system command (shell command) and reading the output of the command in your Perl script. The Unix date command prints the system date and time. Running it at the command line, you get output that looks something like this:
Sun Jul 12 16:55:51 EDT 1998
To run the date command from a Perl program, and read the output of the command, all you need are a few lines of code like this:
open(DATE, "date|"); $theDate = <DATE>; close(DATE);
Listing 1 (above): A short code snippet that runs the external date command, and then read it's output into the variable $theDate.
This Perl example runs the Unix/Linux date command, and then reads from the command pipeline. I like to think of this as being just like opening up a file for read access.
Next, the output of the date command is read into the Perl variable $theDate
. Here we're just using the line reading operator <> to read from the pipeline. Then when we're done reading from the pipe we simply close it.
A more complicated Perl pipe example - reading the output of the ps command
To make this a little more complicated, let's assume that we want to read the output of the ps -f
command in a Perl script. Typical output of this command looks like this:
UID PID PPID CLS PRI C STIME TTY TIME COMD root 8853 8842 TS 70 0 16:55:42 pts007 0:00 ksh root 8875 8853 59 0 17:08:35 pts007 0:00 ps -f root 8842 8840 TS 70 0 16:55:22 pts007 0:00 -sh
Listing 2 (above): Sample output of the Unix ps -f command.
Reading this command output in a Perl script is a little more difficult because there are multiple lines of output to contend with, but this is easily taken care of by using a while loop to handle the reading. Listing 3 shows a code snippet that (a) runs the ps -f command, and (b) reads the output of the command:
open(PS_F, "ps -f|"); while (<PS_F>) { ($uid,$pid,$ppid,$restOfLine) = split; # do whatever I want with the variables here ... } close(PS_F);
Listing 3 (above): A short code snippet that runs the external ps -f command, and then reads it's output with the help of a while loop.
In Listing 3 we do the same thing we did in Listing 1 - we run an external command, and then read from it. However, in this case we know that we're going to get multiple lines of output from the command, so we read from the pipeline using a while
loop.
After setting up the while
loop, we take our example a step farther by reading the output into four variables: $user
, $pid
, $ppid
, and $restOfLine
. Here I'm assuming that I'm only really interested in the first three columns of output, and the other columns are of no concern. This is easily accomplished with Perl's split
function.
Note that in this example I'm taking advantage of a few of Perl's shortcuts. The output of the ps -f
command is read automagically read into the special variable $_
. (This is a convenience feature of Perl.) Then, because the output is stored in the $_
variable, I can just write split
instead of "split($_);
".
After reading the data into these variables, I can do whatever I want with the information in the loop. This part I leave to your imagination - and your application.
Reader Follow-Up Comments:
From Dave Cross and others:
Things like your date example are easier using backticks:
$theDate = `date`;
This can also be used for the ps example.
You also imply that just using split is the same as split($_)
when it's really split(/\s+/,$_)
.