📄 ch16_08.htm
字号:
<HTML><HEAD><TITLE>Recipe 16.7. Reading STDERR from a Program (Perl Cookbook)</TITLE><METANAME="DC.title"CONTENT="Perl Cookbook"><METANAME="DC.creator"CONTENT="Tom Christiansen & Nathan Torkington"><METANAME="DC.publisher"CONTENT="O'Reilly & Associates, Inc."><METANAME="DC.date"CONTENT="1999-07-02T01:43:50Z"><METANAME="DC.type"CONTENT="Text.Monograph"><METANAME="DC.format"CONTENT="text/html"SCHEME="MIME"><METANAME="DC.source"CONTENT="1-56592-243-3"SCHEME="ISBN"><METANAME="DC.language"CONTENT="en-US"><METANAME="generator"CONTENT="Jade 1.1/O'Reilly DocBook 3.0 to HTML 4.0"><LINKREV="made"HREF="mailto:online-books@oreilly.com"TITLE="Online Books Comments"><LINKREL="up"HREF="ch16_01.htm"TITLE="16. Process Management and Communication"><LINKREL="prev"HREF="ch16_07.htm"TITLE="16.6. Preprocessing Input"><LINKREL="next"HREF="ch16_09.htm"TITLE="16.8. Controlling Input and Output of Another Program"></HEAD><BODYBGCOLOR="#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="Perl Cookbook"><area shape="rect" coords="629,-11,726,25" href="jobjects/fsearch.htm" alt="Search this book" /></map><div class="navbar"><p><TABLEWIDTH="684"BORDER="0"CELLSPACING="0"CELLPADDING="0"><TR><TDALIGN="LEFT"VALIGN="TOP"WIDTH="228"><ACLASS="sect1"HREF="ch16_07.htm"TITLE="16.6. Preprocessing Input"><IMGSRC="../gifs/txtpreva.gif"ALT="Previous: 16.6. Preprocessing Input"BORDER="0"></A></TD><TDALIGN="CENTER"VALIGN="TOP"WIDTH="228"><B><FONTFACE="ARIEL,HELVETICA,HELV,SANSERIF"SIZE="-1"><ACLASS="chapter"REL="up"HREF="ch16_01.htm"TITLE="16. Process Management and Communication"></A></FONT></B></TD><TDALIGN="RIGHT"VALIGN="TOP"WIDTH="228"><ACLASS="sect1"HREF="ch16_09.htm"TITLE="16.8. Controlling Input and Output of Another Program"><IMGSRC="../gifs/txtnexta.gif"ALT="Next: 16.8. Controlling Input and Output of Another Program"BORDER="0"></A></TD></TR></TABLE></DIV><DIVCLASS="sect1"><H2CLASS="sect1"><ACLASS="title"NAME="ch16-chap16_reading_1">16.7. Reading STDERR from a Program</A></H2><DIVCLASS="sect2"><H3CLASS="sect2"><ACLASS="title"NAME="ch16-pgfId-1284">Problem<ACLASS="indexterm"NAME="ch16-idx-1000006308-0"></A><ACLASS="indexterm"NAME="ch16-idx-1000006308-1"></A><ACLASS="indexterm"NAME="ch16-idx-1000006308-2"></A><ACLASS="indexterm"NAME="ch16-idx-1000006308-3"></A><ACLASS="indexterm"NAME="ch16-idx-1000006308-4"></A></A></H3><PCLASS="para">You want to run a program as you would with <CODECLASS="literal">system</CODE>, backticks, or <CODECLASS="literal">open</CODE>, but you don't want its STDERR to be sent to your STDERR. You would like to be able to either ignore or read the STDERR.</P></DIV><DIVCLASS="sect2"><H3CLASS="sect2"><ACLASS="title"NAME="ch16-pgfId-1290">Solution</A></H3><PCLASS="para">Use the shell's numeric redirection and duplication syntax for file descriptors. (We don't check the return value from <CODECLASS="literal">open</CODE> here, to make the examples easier to read, but you should always check it in your programs!)</P><PCLASS="para">To capture a command's STDERR and STDOUT together:</P><PRECLASS="programlisting">$output = `cmd 2>&1`; # with backticks# or$pid = open(PH, "cmd 2>&1 |"); # with an open pipewhile (<PH>) { } # plus a read</PRE><PCLASS="para">To capture a command's STDOUT and discard its STDERR:</P><PRECLASS="programlisting">$output = `cmd 2>/dev/null`; # with backticks# or$pid = open(PH, "cmd 2>/dev/null |"); # with an open pipewhile (<PH>) { } # plus a read</PRE><PCLASS="para">To capture a command's STDERR and discard its STDOUT:</P><PRECLASS="programlisting">$output = `cmd 2>&1 1>/dev/null`; # with backticks# or$pid = open(PH, "cmd 2>&1 1>/dev/null |"); # with an open pipewhile (<PH>) { } # plus a read</PRE><PCLASS="para">To exchange a command's STDOUT and STDERR, i.e., capture the STDERR but have its STDOUT come out on our old STDERR:</P><PRECLASS="programlisting">$output = `cmd 3>&1 1>&2 2>&3 3>&-`; # with backticks# or$pid = open(PH, "cmd 3>&1 1>&2 2>&3 3>&-|"); # with an open pipewhile (<PH>) { } # plus a read</PRE><PCLASS="para">To read both a command's STDOUT and its STDERR separately, it's easiest and safest to redirect them separately to files, and then read from those files when the program is done:</P><PRECLASS="programlisting">system("program args 1>/tmp/program.stdout 2>/tmp/program.stderr");</PRE></DIV><DIVCLASS="sect2"><H3CLASS="sect2"><ACLASS="title"NAME="ch16-pgfId-1332">Discussion</A></H3><PCLASS="para">When you launch a command with backticks, a piped <CODECLASS="literal">open</CODE>, or <CODECLASS="literal">system</CODE> on a single string, Perl checks for characters special to the shell. These allow you to redirect the new program's file descriptors. STDIN is file descriptor number 0, STDOUT number 1, and STDERR number 2. You can then use <CODECLASS="literal">2></CODE><ICLASS="filename">file</I> to redirect STDERR to a file. The special notation <CODECLASS="literal">&N</CODE> where N is a file descriptor number is used to redirect to a file descriptor. Therefore, <CODECLASS="literal">2>&1</CODE> points STDERR at STDOUT.</P><PCLASS="para">Here is a table of interesting shell file descriptor redirections:</P><TABLECLASS="informaltable"BORDER="1"CELLPADDING="3"><THEADCLASS="thead"><TRCLASS="row"VALIGN="TOP"><THCLASS="entry"ALIGN="LEFT"ROWSPAN="1"COLSPAN="1"><PCLASS="para">Redirection</P></TH><THCLASS="entry"ALIGN="LEFT"ROWSPAN="1"COLSPAN="1"><PCLASS="para">Meaning</P></TH></TR></THEAD><TBODYCLASS="tbody"><TRCLASS="row"VALIGN="TOP"><TDCLASS="entry"ROWSPAN="1"COLSPAN="1"><PCLASS="para"><CODECLASS="literal">0</dev/null </CODE></P></TD><TDCLASS="entry"ROWSPAN="1"COLSPAN="1"><PCLASS="para"><CODECLASS="literal">Make STDIN give immediate EOF</CODE></P></TD></TR><TRCLASS="row"VALIGN="TOP"><TDCLASS="entry"ROWSPAN="1"COLSPAN="1"><PCLASS="para"><CODECLASS="literal">1>/dev/null </CODE></P></TD><TDCLASS="entry"ROWSPAN="1"COLSPAN="1"><PCLASS="para"><CODECLASS="literal">Discard STDOUT</CODE></P></TD></TR><TRCLASS="row"VALIGN="TOP"><TDCLASS="entry"ROWSPAN="1"COLSPAN="1"><PCLASS="para"><CODECLASS="literal">2>/dev/null </CODE></P></TD><TDCLASS="entry"ROWSPAN="1"COLSPAN="1"><PCLASS="para"><CODECLASS="literal">Discard STDERR</CODE></P></TD></TR><TRCLASS="row"VALIGN="TOP"><TDCLASS="entry"ROWSPAN="1"COLSPAN="1"><PCLASS="para"><CODECLASS="literal">2>&1 </CODE></P></TD><TDCLASS="entry"ROWSPAN="1"COLSPAN="1"><PCLASS="para"><CODECLASS="literal">Send STDERR to STDOUT instead</CODE></P></TD></TR><TRCLASS="row"VALIGN="TOP"><TDCLASS="entry"ROWSPAN="1"COLSPAN="1"><PCLASS="para"><CODECLASS="literal">2>&- </CODE></P></TD><TDCLASS="entry"ROWSPAN="1"COLSPAN="1"><PCLASS="para"><CODECLASS="literal">Close STDERR (not recommended)</CODE></P></TD></TR><TRCLASS="row"VALIGN="TOP"><TDCLASS="entry"ROWSPAN="1"COLSPAN="1"><PCLASS="para"><CODECLASS="literal">3<>/dev/tty </CODE></P></TD><TDCLASS="entry"ROWSPAN="1"COLSPAN="1"><PCLASS="para"><CODECLASS="literal">Open fd 3 to</CODE><ICLASS="filename"> /dev/tty</I><CODECLASS="literal"> in read-write mode</CODE></P></TD></TR></TBODY></TABLE><PCLASS="para">Using this, let's examine the most complicated of the redirection sequences from the solution section:</P><PRECLASS="programlisting">$output = `cmd 3>&1 1>&2 2>&3 3>&-`; </PRE><PCLASS="para">There are four steps here:</P><DLCLASS="variablelist"><DTCLASS="term">Step A: <CODECLASS="literal">3>&1</CODE> </DT><DDCLASS="listitem"><PCLASS="para">Make a new file descriptor, number 3, be a copy of number 1. This saves where STDOUT had been destined in the new file descriptor we've just opened.</P></DD><DTCLASS="term">Step B: <CODECLASS="literal">1>&2</CODE> </DT><DDCLASS="listitem"><PCLASS="para">Make STDOUT go wherever STDERR had been going. We still have the saved destination squirreled away in descriptor 3.</P></DD><DTCLASS="term">Step C: <CODECLASS="literal">2>&3</CODE></DT><DDCLASS="listitem"><PCLASS="para">Make file descriptor 2 a copy of number 3. That means that STDERR is now going out where STDOUT had been originally going.</P></DD><DTCLASS="term">Step D: <CODECLASS="literal">3>&-</CODE> </DT><DDCLASS="listitem"><PCLASS="para">Since we're done moving streams around, keep everything nice and tidy and close our temporary file descriptor. This avoids file descriptor leaks.</P></DD></DL><PCLASS="para">If that's confusing, it might help to think in terms of regular variables and a sequence of assignment statements, with <CODECLASS="literal">$fd1</CODE> representing STDOUT and <CODECLASS="literal">$fd2</CODE> representing STDERR. If you wanted to exchange the two variables, you'd use a temporary to hold one value. That's all we're doing here.</P><PRECLASS="programlisting">$fd3 = $fd1;$fd1 = $fd2;$fd2 = $fd3;$fd3 = undef;</PRE><PCLASS="para">When all's said and done, the string returned from the backticks is the command's STDERR, and its STDOUT has been diverted to the original STDERR.</P><PCLASS="para">Ordering is important in all these examples. That's because the shell processes file descriptor redirections in strictly left to right order.</P><PRECLASS="programlisting">system("prog args 1>tmpfile 2>&1");system("prog args 2>&1 1>tmpfile");</PRE><PCLASS="para">The first command sends both standard out and standard error to the temporary file. The second command sends only the old standard output there, and the old standard error shows up on the old standard out. If that's confusing, think in terms of assignments to variables representing file descriptors:</P><PRECLASS="programlisting"># system ("prog args 1>tmpfile 2>&1");$fd1 = "tmpfile"; # change stdout destination first$fd2 = $fd1; # now point stderr there, too</PRE><PCLASS="para">is very different from:</P><PRECLASS="programlisting"># system("prog args 2>&1 1>tmpfile");$fd2 = $fd1; # stderr same destination as stdout$fd1 = "tmpfile"; # but change stdout destination <ACLASS="indexterm"NAME="ch16-idx-1000006310-0"></A><ACLASS="indexterm"NAME="ch16-idx-1000006310-1"></A><ACLASS="indexterm"NAME="ch16-idx-1000006310-2"></A><ACLASS="indexterm"NAME="ch16-idx-1000006310-3"></A><ACLASS="indexterm"NAME="ch16-idx-1000006310-4"></A></PRE></DIV><DIVCLASS="sect2"><H3CLASS="sect2"><ACLASS="title"NAME="ch16-pgfId-1384">See Also</A></H3><PCLASS="para">Your system's <EMCLASS="emphasis">sh </EM>(1) manpage (if you have one) for details about file descriptor redirection; the <ACLASS="olink"HREF="../prog/ch03_167.htm"><CODECLASS="literal">system</CODE></A> function in <ACLASS="olink"HREF="../prog/ch03_01.htm">Chapter 3</A> of <ACLASS="citetitle"HREF="../prog/index.htm"TITLE="Programming Perl"><CITECLASS="citetitle">Programming Perl</CITE></A> and in <ICLASS="filename">perlfunc </I>(1)</P></DIV></DIV><DIVCLASS="htmlnav"><P></P><HRALIGN="LEFT"WIDTH="684"TITLE="footer"><TABLEWIDTH="684"BORDER="0"CELLSPACING="0"CELLPADDING="0"><TR><TDALIGN="LEFT"VALIGN="TOP"WIDTH="228"><ACLASS="sect1"HREF="ch16_07.htm"TITLE="16.6. Preprocessing Input"><IMGSRC="../gifs/txtpreva.gif"ALT="Previous: 16.6. Preprocessing Input"BORDER="0"></A></TD><TDALIGN="CENTER"VALIGN="TOP"WIDTH="228"><ACLASS="book"HREF="index.htm"TITLE="Perl Cookbook"><IMGSRC="../gifs/txthome.gif"ALT="Perl Cookbook"BORDER="0"></A></TD><TDALIGN="RIGHT"VALIGN="TOP"WIDTH="228"><ACLASS="sect1"HREF="ch16_09.htm"TITLE="16.8. Controlling Input and Output of Another Program"><IMGSRC="../gifs/txtnexta.gif"ALT="Next: 16.8. Controlling Input and Output of Another Program"BORDER="0"></A></TD></TR><TR><TDALIGN="LEFT"VALIGN="TOP"WIDTH="228">16.6. Preprocessing Input</TD><TDALIGN="CENTER"VALIGN="TOP"WIDTH="228"><ACLASS="index"HREF="index/index.htm"TITLE="Book Index"><IMGSRC="../gifs/index.gif"ALT="Book Index"BORDER="0"></A></TD><TDALIGN="RIGHT"VALIGN="TOP"WIDTH="228">16.8. Controlling Input and Output of Another Program</TD></TR></TABLE><HRALIGN="LEFT"WIDTH="684"TITLE="footer"><FONTSIZE="-1"></DIV<!-- LIBRARY NAV BAR --> <img src="../gifs/smnavbar.gif" usemap="#library-map" border="0" alt="Library Navigation Links"><p> <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 + -