Recipe 16.1 Gathering Output from a Program

16.1.1 Problem

You want to run a program and collect its output into a variable.

16.1.2 Solution

Either use backticks:

$output = `program args`;   # collect output into one multiline string @output = `program args`;   # collect output into array, one line per element

or use Recipe 16.4:

open(my $fh, "-|", "program", @args)     or die "Can't run program: $!\n"; while (<$fh>) {     $output .= $_; } close $fh;

16.1.3 Discussion

The backticks are a convenient way to run other programs and gather their output. The backticks do not return until the called program exits. Perl goes to some trouble behind the scenes to collect the output, so it is inefficient to use the backticks and ignore their return value:

`fsck -y /dev/rsd1a`;       # BAD AND SCARY

The backtick operator calls the shell to run the command. This makes it unsafe when used in a program with special privileges, but lets you use shell wildcards in the command:

@files = `ls -1 /music/*.mp3`;

If you want to read the output of a wildcarded command line as it's generated (and don't mind the potential security problems), use this form of open:

open(README, "ls -l /music/*.mp3 |") or die "Can't run program: $!\n"; while(<README>) {     # the latest line is in $_ } close(README);

In versions of Perl before 5.8, this two-argument form of open was the only one available to you. In those versions of Perl, you wrote the solution as:

open(FH, "program @args |")   or die "Can't run program: $!\n";

Here's a low-level workaround, using pipe (to create two connected filehandles), fork (to split off a new process), and exec (to replace the new process with the program to read from):

use POSIX qw(:sys_wait_h); my  ($readme, $writeme); pipe $readme, $writeme; if ($pid = fork) {     # parent     $SIG{CHLD} = sub { 1 while ( waitpid(-1, WNOHANG)) > 0 };     close $writeme; } else {     die "cannot fork: $!" unless defined $pid;     # child     open(STDOUT, ">&=", $writeme)   or die "Couldn't redirect STDOUT: $!";     close $readme;     exec($program, $arg1, $arg2)    or die "Couldn't run $program : $!\n"; } while (<$readme>) {     $string .= $_;     # or  push(@strings, $_); } close($readme);

There's no reason to prefer this over the open "-|" code in the Solution, except that the low-level workaround lets you change signal disposition before you launch the new program. For example, you could disable the INT signal in the child so that only the parent process receives it.

16.1.4 See Also

The section on The section on "Talking to Yourself" in Chapter 16 of Programming Perl or perlsec(1); Recipe 16.2; Recipe 16.4; Recipe 16.10; Recipe 16.19; Recipe 19.5



Perl Cookbook
Perl Cookbook, Second Edition
ISBN: 0596003137
EAN: 2147483647
Year: 2003
Pages: 501

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net