Документация
HTML CSS PHP PERL другое

Section 14.4.  Using Backquotes to Capture Output

 
Previous
Table of Contents
Next

14.4. Using Backquotes to Capture Output

With system and exec, the output of the launched command ends up wherever Perl's standard output is going. Sometimes, it's interesting to capture that output as a string value to perform further processing. That's done by creating a string using backquotes instead of single or double quotes:

    my $now = `date`;             # grab the output of date
    print "The time is now $now"; # newline already present

Normally, this date command spits out a string approximately 30 characters long to its standard output, giving the current date and time followed by a newline. When we've placed date between backquotes, Perl executes the date command, arranging to capture its standard output as a string value and, in this case, assigned to the $now variable.

This is similar to the Unix shell's meaning for backquotes. However, the shell performs the additional job of ripping off the final end-of-line to make it easier to use the value as part of other things. Perl is honest; it gives the real output. To get the same result in Perl, add an additional chomp operation on the result:

    chomp(my $no_newline_now = `date`);
    print "A moment ago, it was $no_newline_now, I think.\n";

The value beween backquotes is like the single-argument form of system[*] and is interpreted as a double-quoted string, meaning that backslash-escapes and variables are expanded appropriately.[Section 14.4.  Using Backquotes to Capture Output] For example, to fetch the Perl documentation on a list of Perl functions, we might invoke the perldoc command repeatedly, each time with a different argument:

[*] That is, it's always interpreted by the One True Shell (/bin/sh) or an alternative, as with system.

[Section 14.4.  Using Backquotes to Capture Output] If you want to pass a real backslash to the shell, you'll need to use two. If you need to pass two (which happens frequently on Windows systems), you'll need to use four.

    my @functions = qw{ int rand sleep length hex eof not exit sqrt umask };
    my %about;
     
    foreach (@functions) {
      $about{$_} = `perldoc -t -f $_`;
    }

$_ will be a different value for each invocation, letting us grab the output of a different command varying only in one of its parameters. If you haven't seen some of these functions, it might be useful to look them up in the documentation to see what they do.

There's no easy equivalent of single quotes for backquotes;[*] variable references and backslash items are always expanded. There's no easy equivalent of the multiple-argument version of system where a shell is never involved. If the command inside the backquotes is complex enough, a Unix Bourne Shell (or whatever your system uses) is invoked to interpret the command automatically.

[*] For harder methods, you can place your string inside qx'...' delimiters, or you can put it all in a variable using a single-quoted string and interpolate that string into a backquoted string since the interpolation will be only one level.

At the risk of introducing the behavior by demonstrating how not to do it, we'd like to suggest you avoid using backquotes in a place where the value isn't being captured[Section 14.4.  Using Backquotes to Capture Output] as in this example:

[Section 14.4.  Using Backquotes to Capture Output] This is called a "void" context.

    print "Starting the frobnitzigator:\n";
    `frobnitz -enable`; # please don't do this!
    print "Done!\n";

The problem is that Perl has to work harder to capture the output of this command, even when you're throwing it away. Also, you lose the option to use multiple arguments to system to control the argument list. So from both a security standpoint and an efficiency viewpoint, use system instead.

Standard error of a backquoted command is inherited from Perl's current standard error output. If the command spits out error messages to standard error, you'll probably see them on the terminal, which could be confusing to the user who hasn't invoked the frobnitz command. If you want to capture error messages with standard output, you can use the shell's normal "merge standard error to the current standard output," which is spelled 2>&1 in the normal Unix shell:

    my $output_with_errors = `frobnitz -enable 2>&1`;

This will make the standard error output intermingled with the standard output, much as it appears on the terminal, though possibly in a different sequence because of buffering. If you need the output and the error output separated, you will find harder-to-type solutions.[*] Similarly, standard input is inherited from Perl's current standard input. The commands we typically use with backquotes don't read standard input, so that's rarely a problem. However, let's say the date command asked which time zone (as we imagined earlier). That'll be a problem because the prompt for "which time zone" will be sent to standard output, which is being captured as part of the value. Then, the date command will want to read from standard input. Since the user has never seen the prompt, he doesn't know he should be typing anything. Soon, the user calls you up and tells you your program is stuck.

[*] Such as IPC::Open3 in the standard Perl library, or writing your own forking code as you will see later.

So, stay away from commands that read standard input. If you're not sure whether something reads from standard input, add a redirection from /dev/null for input like this:

    my $result = `some_questionable_command arg arg argh </dev/null`;

The child shell will redirect input from /dev/null, and the grandchild questionable command will at worst read and immediately get an end-of-file.

14.4.1. Using Backquotes in a List Context

If the output from a command has multiple lines, the scalar use of backquotes returns it as a single long string containing newline characters. However, using the same backquoted string in a list context yields a list containing one line of output per element.

For example, the Unix who command normally spits out a line of text for each current login on the system as follows:

    merlyn     tty/42     Dec 7  19:41
    rootbeer   console    Dec 2  14:15
    rootbeer   tty/12     Dec 6  23:00

The left column is the username, the middle column is the tty name (that is, the name of the user's connection to the machine), and the rest of the line is the date and time of login (and possibly remote login information but not in this example). In a scalar context, we get all that at once, which we would need to split up:

    my $who_text = `who`;

But in a list context, we automatically get the data broken up by lines:

    my @who_lines = `who`;

We'll have a number of separate elements in @who_lines, each one terminated by a newline. Adding a chomp around the outside of that will rip off those newlines, but let's go in a different direction. If we put that as part of the value for a foreach, we'll iterate over the lines automatically, placing each one in $_:

    foreach (`who`) {
      my($user, $tty, $date) = /(\S+)\s+(\S+)\s+(.*)/;
      $ttys{$user} .= "$tty at $date\n";
    }

This loop will iterate three times for the data above. (Your system will probably have more than three active logins at any given time.) The first statement in the loop is a regular expression match, and in the absence of the binding operator (=~), that's matching against $_, which is good because that's where the data is.

The regular expression is looking for a nonblank word, some whitespace, a nonblank word, some whitespace, and the rest of the line up to, but not including, the newline (since dot doesn't match newline by default).[*] That's good because that's what $_ looks like each time through the loop. That'll make $1 be "merlyn", $2 be "tty/42", and $3 be "Dec 7 19:41" as a successful match on the first time through the loop.

[*] Now you can see why dot doesn't match newline by default. It makes it easy to write patterns like this one in which we don't have to worry about a newline at the end of the string.

However, this regular expression match is in a list context, so we'll get the list of memories instead of the true/false "did it match" value, as described in Chapter 8. So, $user ends up being "merlyn", and so on.

The second statement inside the loop stores away the tty and date information, appending to a (possibly undef) value in the hash because a user might be logged in more than once as user "rootbeer" was in our example.

    Previous
    Table of Contents
    Next
    © 2000- NIV