📄 ch14_04.htm
字号:
<html><head><title>Using Backquotes to Capture Output (Learning Perl, 3rd Edition)</title><link rel="stylesheet" type="text/css" href="../style/style1.css" /><meta name="DC.Creator" content="Randal L. Schwartz and Tom Phoenix" /><meta name="DC.Format" content="text/xml" scheme="MIME" /><meta name="DC.Language" content="en-US" /><meta name="DC.Publisher" content="O'Reilly & Associates, Inc." /><meta name="DC.Source" scheme="ISBN" content="0596001320L" /><meta name="DC.Subject.Keyword" content="stuff" /><meta name="DC.Title" content="Learning Perl, 3rd Edition" /><meta name="DC.Type" content="Text.Monograph" /></head><body bgcolor="#ffffff"><img alt="Book Home" border="0" src="gifs/smbanner.gif" usemap="#banner-map" /><map name="banner-map"><area shape="rect" coords="1,-2,616,66" href="index.htm" alt="Learning Perl, 3rd Edition" /><area shape="rect" coords="629,-11,726,25" href="jobjects/fsearch.htm" alt="Search this book" /></map><div class="navbar"><table width="684" border="0"><tr><td align="left" valign="top" width="228"><a href="ch14_03.htm"><img alt="Previous" border="0" src="../gifs/txtpreva.gif" /></a></td><td align="center" valign="top" width="228"><a href="index.htm"></a></td><td align="right" valign="top" width="228"><a href="ch14_05.htm"><img alt="Next" border="0" src="../gifs/txtnexta.gif" /></a></td></tr></table></div><h2 class="sect1">14.4. Using Backquotes to Capture Output</h2><p><a name="INDEX-965" /> <a name="INDEX-966" /><a name="INDEX-967" />With<a name="INDEX-968" />both <tt class="literal">system</tt> and<tt class="literal">exec</tt>, the output of the launched command ends upwherever Perl's standard output is going. Sometimes, it'sinteresting to capture that output as a string value to performfurther processing. And that's done simply by creating a stringusing backquotes instead of single or double quotes:</p><blockquote><pre class="code">my $now = `date`; # grab the output of dateprint "The time is now $now"; # newline already present</pre></blockquote><p>Normally, this <i class="command">date</i> command spits out a stringapproximately 30 characters long to its standard output, giving thecurrent date and time followed by a newline. When we've placed<i class="command">date</i> between backquotes, Perl executes the<i class="command">date</i> command, arranging for its standard output tobe captured as a string value, and in this case assigned to the<tt class="literal">$now</tt><a name="INDEX-969" /> variable.</p><p>This is very similar to the Unix shell's meaning forbackquotes. However, the shell also performs the additional job ofripping off the final end-of-line to make it easier to use the valueas part of other things. Perl is honest; it gives the real output. Toget the same result in Perl, we can simply add an additional<tt class="literal">chomp</tt> operation on the result:</p><blockquote><pre class="code">chomp(my $no_newline_now = `date`);print "A moment ago, it was $no_newline_now, I think.\n";</pre></blockquote><p>The value beween backquotes is just like the single-argument form ofsystem,<a href="#FOOTNOTE-316">[316]</a> and isinterpreted as a double-quoted string, meaning that backslash-escapesand variables are expanded appropriately.<a href="#FOOTNOTE-317">[317]</a> Forexample, to fetch the Perl documentation on a list of Perl functions,we might invoke the <i class="command">perldoc</i> command repeatedly,each time with a different argument:</p><blockquote class="footnote"> <a name="FOOTNOTE-316" /><p>[316]That is, it's also always interpretedby the One True Shell (<em class="emphasis">/bin/sh</em>) or alternative,as with <tt class="literal">system</tt>.</p> </blockquote><blockquote class="footnote"> <a name="FOOTNOTE-317" /><p>[317]So, if youwant to pass a real backslash to the shell, you'll need to usetwo. If you need to pass two (which happens frequently on Windowssystems), you'll need to use four. </p> </blockquote><blockquote><pre class="code">my @functions = qw{ int rand sleep length hex eof not exit sqrt umask };my %about;foreach (@functions) { $about{$_} = `perldoc -t -f $_`;}</pre></blockquote><p>Note that <tt class="literal">$_</tt> will be a different value for eachinvocation, letting us grab the output of a different command varyingonly in one of its parameters. Also note that if you haven'tseen some of these functions yet, it might be useful to look them upin the documentation to see what they do!</p><p>There's no easy equivalent of single quotes forbackquotes<a href="#FOOTNOTE-318">[318]</a> ; variable references and backslash items are alwaysexpanded. Also, there's no easy equivalent of themultiple-argument version of <tt class="literal">system</tt> (where ashell is never involved). If the command inside the backquotes iscomplex enough, a Unix Bourne Shell (or whatever your system usesinstead) is invoked to interpret the command automatically.</p><blockquote class="footnote"> <a name="FOOTNOTE-318" /><p>[318]For a couple of harder ways, you can placeyour string inside <tt class="literal">qx'...'</tt>delimiters, or you canput it all in a variable using a single-quoted string, theninterpolate <em class="emphasis">that</em> string into a backquotedstring, since the interpolation will be only one level.</p></blockquote><p>At the risk of actually introducing the behavior by demonstrating how<em class="emphasis">not</em> to do it, we'd also like to suggestthat you avoid using backquotes in a place where the valueisn't being captured.<a href="#FOOTNOTE-319">[319]</a> For example:</p><blockquote class="footnote"> <a name="FOOTNOTE-319" /><p>[319]This is called a"void" context.</p> </blockquote><blockquote><pre class="code">print "Starting the frobnitzigator:\n";`frobnitz -enable`; # please don't do this!print "Done!\n";</pre></blockquote><p>The problem is that Perl has to work a bit harder to capture theoutput of this command, even when you're just throwing it away,and then you also lose the option to use multiple arguments to<tt class="literal">system</tt> to precisely control the argument list.So from both a security standpoint and an efficiency viewpoint, justuse <tt class="literal">system</tt> instead, please.</p><p>Standard error of a backquoted command is inherited from Perl'scurrent standard error output. If the command spits out errormessages to standard error, you'll probably see them on theterminal, which could be confusing to the user who hasn'tpersonally invoked the <i class="command">frobnitz</i> command. If youwant to capture <a name="INDEX-970" />error messages with standard output, youcan use the shell's normal "merge standard error to thecurrent standard output," which is spelled<tt class="literal">2>&1</tt> in the normal Unix shell:</p><blockquote><pre class="code">my $output_with_errors = `frobnitz -enable 2>&1`;</pre></blockquote><p>Note that this will make the standard error output intermingled withthe standard output, much as it appears on the terminal (althoughpossibly in a slightly different sequence because of buffering). Ifyou need the output and the error output separated, there are manyharder-to-type solutions.<a href="#FOOTNOTE-320">[320]</a></p><blockquote class="footnote"> <a name="FOOTNOTE-320" /><p>[320]Such as<tt class="literal">IPC::Open3</tt> in the standard Perl library, orwriting your own forking code, as we will see later.</p></blockquote><p>Similarly, standard input is inherited from Perl's currentstandard input. Most commands we typically use with backquotes do notread standard input, so that's rarely a problem. However,let's say the <i class="command">date</i> command asked which timezone (as we imagined earlier). That'll be a problem, becausethe prompt for "which time zone" will be sent to standardoutput, which is being captured as part of the value, and then the<i class="command">date</i> command will start trying to read fromstandard input. But since the user has never seen the prompt, he orshe doesn't know to be typing anything! Pretty soon, the usercalls you up and tells you that your program is stuck.</p><p>So, stay away from commands that read standard input. If you'renot sure whether something reads from standard input, then add aredirection from <em class="filename">/dev/null</em> for input, like this:</p><blockquote><pre class="code">my $result = `some_questionable_command arg arg argh </dev/null`;</pre></blockquote><p>Then the child shell will redirect input from<em class="filename">/dev/null</em>, and the grandchild questionablecommand will at worst try to read and immediately get an end of file.</p><a name="lperl3-CHP-14-SECT-4.1" /><div class="sect2"><h3 class="sect2">14.4.1. Using Backquotes in a List Context</h3><p><a name="INDEX-971" />If the output from a command has multiplelines, the scalar use of backquotes returns it as a single longstring containing newline characters. However, using the samebackquoted string in a list context yields a list containing one lineof output per element.</p><p>For example, the Unix <i class="command">who</i><a name="INDEX-972" /> command normally spits out aline of text for each current login on the system as follows:</p><blockquote><pre class="code">merlyn tty/42 Dec 7 19:41rootbeer console Dec 2 14:15rootbeer tty/12 Dec 6 23:00</pre></blockquote><p>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 possiblyremote login information, but not in this example). In a scalarcontext, we get all that at once, which we would then need to splitup:</p><blockquote><pre class="code">my $who_text = `who`;</pre></blockquote><p>But in a list context, we automatically get the data broken up bylines:</p><blockquote><pre class="code">my @who_lines = `who`;</pre></blockquote><p>We'll have a number of separate elements in<tt class="literal">@who_lines</tt>, each one terminated by a newline. Ofcourse, adding a <tt class="literal">chomp</tt> around the outside ofthat will rip off those newlines, but let's go a differentdirection. If we put that as part of the value for a<tt class="literal">foreach</tt>, we'll iterate over the linesautomatically, placing each one in <tt class="literal">$_</tt>:</p><blockquote><pre class="code">foreach (`who`) { my($user, $tty, $date) = /(\S+)\s+(\S+)\s+(.*)/; $ttys{$user} .= "$tty at $date\n";}</pre></blockquote><p>This loop will iterate three times for the data above. (Your systemwill probably have more than three active logins at any given time.)Notice that we've got a regular expression match, and in theabsence of the binding operator ("<tt class="literal">=~"</tt>),that's matching against <tt class="literal">$_</tt>, which is goodbecause that's where the data is.</p><p>Also notice the regular expression is looking for a nonblank word,some whitespace, a nonblank word, some whitespace, and then the restof the line up to, but not including, the newline (since dotdoesn't match newline by default).<a href="#FOOTNOTE-321">[321]</a> That's also good, becausethat's what the data looks like each time in<tt class="literal">$_</tt>. That'll make <tt class="literal">$1</tt> be"<tt class="literal">merlyn</tt>", <tt class="literal">$2</tt> be"<tt class="literal">tty/42</tt>", and <tt class="literal">$3</tt> be"<tt class="literal">Dec 7 19:41</tt>", as a successful matchon the first time through the loop.</p><blockquote class="footnote"> <a name="FOOTNOTE-321" /><p>[321]Now you cansee <em class="emphasis">why</em> dot doesn't match newline bydefault. It makes it easy to write patterns like this one, in whichwe don't have to worry about a newline at the end of thestring.</p> </blockquote><p>However, this regular expression match is in a list context, soinstead of returning back a true/false value (as when you have aregular expression match in a scalar context), we take the memoryvariables and bundle them up in sequence as a list. In this case, theright side of that assignment is thus a three-element list, whichhappens to correspond to the three elements of the literal list onthe left, and we get those nice corresponding assignments. So,<tt class="literal">$user</tt> ends up being <tt class="literal">"merlyn"</tt>,and so on.</p><p>The second statement inside the loop simply stores away the tty anddate information, appending to a (possibly <tt class="literal">undef</tt>)value in the hash, because a user might be logged in more than once,as user <tt class="literal">"rootbeer"</tt> was in<a name="INDEX-973" /> ourexample.<a name="INDEX-974" /><a name="INDEX-975" /><a name="INDEX-976" /></p></div><hr width="684" align="left" /><div class="navbar"><table width="684" border="0"><tr><td align="left" valign="top" width="228"><a href="ch14_03.htm"><img alt="Previous" border="0" src="../gifs/txtpreva.gif" /></a></td><td align="center" valign="top" width="228"><a href="index.htm"><img alt="Home" border="0" src="../gifs/txthome.gif" /></a></td><td align="right" valign="top" width="228"><a href="ch14_05.htm"><img alt="Next" border="0" src="../gifs/txtnexta.gif" /></a></td></tr><tr><td align="left" valign="top" width="228">14.3. The Environment Variables</td><td align="center" valign="top" width="228"><a href="index/index.htm"><img alt="Book Index" border="0" src="../gifs/index.gif" /></a></td><td align="right" valign="top" width="228">14.5. Processes as Filehandles</td></tr></table></div><hr width="684" align="left" /><img alt="Library Navigation Links" border="0" src="../gifs/navbar.gif" usemap="#library-map" /><p><p><font size="-1"><a href="copyrght.htm">Copyright © 2002</a> O'Reilly & Associates. All rights reserved.</font></p><map name="library-map"><area shape="rect" coords="1,0,85,94" href="../index.htm"><area shape="rect" coords="86,1,178,103" href="../lwp/index.htm"><area shape="rect" coords="180,0,265,103" href="../lperl/index.htm"><area shape="rect" coords="267,0,353,105" href="../perlnut/index.htm"><area shape="rect" coords="354,1,446,115" href="../prog/index.htm"><area shape="rect" coords="448,0,526,132" href="../tk/index.htm"><area shape="rect" coords="528,1,615,119" href="../cookbook/index.htm"><area shape="rect" coords="617,0,690,135" href="../pxml/index.htm"></map></body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -