ch16_10.htm

来自「By Tom Christiansen and Nathan Torkingto」· HTM 代码 · 共 476 行

HTM
476
字号
<HTML><HEAD><TITLE>Recipe 16.9. Controlling the Input, Output, and Error of Another Program (Perl Cookbook)</TITLE><METANAME="DC.title"CONTENT="Perl Cookbook"><METANAME="DC.creator"CONTENT="Tom Christiansen &amp; Nathan Torkington"><METANAME="DC.publisher"CONTENT="O'Reilly &amp; Associates, Inc."><METANAME="DC.date"CONTENT="1999-07-02T01:43:52Z"><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_09.htm"TITLE="16.8. Controlling Input and Output of Another Program"><LINKREL="next"HREF="ch16_11.htm"TITLE="16.10. Communicating Between Related Processes"></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_09.htm"TITLE="16.8. Controlling Input and Output of Another Program"><IMGSRC="../gifs/txtpreva.gif"ALT="Previous: 16.8. Controlling Input and Output of Another Program"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_11.htm"TITLE="16.10. Communicating Between Related Processes"><IMGSRC="../gifs/txtnexta.gif"ALT="Next: 16.10. Communicating Between Related Processes"BORDER="0"></A></TD></TR></TABLE></DIV><DIVCLASS="sect1"><H2CLASS="sect1"><ACLASS="title"NAME="ch16-chap16_controlling_1">16.9. Controlling the Input, Output, and Error of Another Program</A></H2><DIVCLASS="sect2"><H3CLASS="sect2"><ACLASS="title"NAME="ch16-pgfId-1480">Problem<ACLASS="indexterm"NAME="ch16-idx-1000006340-0"></A><ACLASS="indexterm"NAME="ch16-idx-1000006340-1"></A></A></H3><PCLASS="para">You want full control over a command's input, output, and error streams.</P></DIV><DIVCLASS="sect2"><H3CLASS="sect2"><ACLASS="title"NAME="ch16-pgfId-1486">Solution</A></H3><PCLASS="para">Carefully use the standard <ACLASS="indexterm"NAME="ch16-idx-1000006342-0"></A>IPC::Open3 module, possibly in conjunction with the IO::Select module. (IO::Select is new as of the 5.004 distribution.)</P></DIV><DIVCLASS="sect2"><H3CLASS="sect2"><ACLASS="title"NAME="ch16-pgfId-1492">Discussion</A></H3><PCLASS="para">If you're interested in only one of the program's STDIN, STDOUT, or STDERR, the task is simple. When you want to manage two or more of these, however, it abruptly stops being simple. Multiplexing multiple I/O streams is never a pretty picture. Here's an easy workaround:</P><PRECLASS="programlisting">@all = `($cmd | sed -e 's/^/stdout: /' ) 2&gt;&amp;1`;for (@all) { push @{ s/stdout: // ? \@outlines : \@errlines }, $_ }print &quot;STDOUT:\n&quot;, @outlines, &quot;\n&quot;;print &quot;STDERR:\n&quot;, @errlines, &quot;\n&quot;;</PRE><PCLASS="para">If you don't have <EMCLASS="emphasis">sed</EM> on your system, you'll find that for simple cases like this, <EMCLASS="emphasis">perl -pe</EM> works just as well as <EMCLASS="emphasis">sed -e</EM>.</P><PCLASS="para">However, that's not really simultaneous processing. All we're doing is marking STDOUT lines with <CODECLASS="literal">&quot;stdout:&quot;</CODE> and then stripping them back out once we've read all the STDOUT and STDERR the program produced.</P><PCLASS="para">You can use the standard IPC::Open3 module for this. Mysteriously, the argument order is different for IPC::Open3 than for IPC::Open2.</P><PRECLASS="programlisting">open3(*WRITEHANDLE, *READHANDLE, *ERRHANDLE, &quot;program to run&quot;);</PRE><PCLASS="para">Using this has even <EMCLASS="emphasis">more</EM> potential for chaos than using <CODECLASS="literal">open2</CODE>. If you're reading the program's STDERR as it is trying to write more than one buffer's worth to its STDOUT, the program will block on the write because its buffers are full, and you will block on the read because there's nothing available.</P><PCLASS="para">You can avoid this deadlock by mimicking <CODECLASS="literal">open3</CODE> with <CODECLASS="literal">fork</CODE>, <CODECLASS="literal">open</CODE>, and <CODECLASS="literal">exec</CODE>; making all the filehandles unbuffered; and using <CODECLASS="literal">sysread</CODE>, <CODECLASS="literal">syswrite</CODE>, and <CODECLASS="literal">select</CODE> to decide which readable filehandle to read a byte from. This makes your program slower and bulkier, though, and it doesn't solve the classic <CODECLASS="literal">open2</CODE> deadlock where each program is expecting the other to say something.</P><PRECLASS="programlisting">use IPC::Open3;$pid = open3(*HIS_IN, *HIS_OUT, *HIS_ERR, $cmd);close(HIS_IN);  # give end of file to kid, or feed him@outlines = &lt;HIS_OUT&gt;;              # read till EOF@errlines = &lt;HIS_ERR&gt;;              # XXX: block potential if massiveprint &quot;STDOUT:\n&quot;, @outlines, &quot;\n&quot;;print &quot;STDERR:\n&quot;, @errlines, &quot;\n&quot;;</PRE><PCLASS="para">As if deadlock weren't bad enough, this approach is subtly error-prone. There are at least three worrisome situations: both the child and the parent trying to read at the same time, causing deadlock; full buffers causing the child to block as it tries to write to STDERR while the parent is blocked trying to read from the child's STDOUT; and full buffers causing the parent to block writing to the child's STDIN while the child is blocked writing to either its STDOUT or STDERR. The first problem is generally unsolvable, although you can work around it by setting timers with <CODECLASS="literal">alarm</CODE> and preventing blocking operations from restarting if a <CODECLASS="literal">SIGALRM</CODE> is received.</P><PCLASS="para">We use the <ACLASS="indexterm"NAME="ch16-idx-1000006343-0"></A>IO::Select module (you could also do this with the built-in function <CODECLASS="literal">select</CODE>) to learn which filehandles (if any) can be read from. This solves the second problem, but not the third. To solve the third, you also need <CODECLASS="literal">alarm</CODE> and non-restarting <CODECLASS="literal">SIGALRM</CODE>.</P><PCLASS="para">If you want to send input to the program, read its output, and either read or ignore its error, you need to work much harder. (See <ACLASS="xref"HREF="ch16_10.htm#ch16-19570"TITLE="cmd3sel">Example 16.2</A>.)</P><DIVCLASS="example"><H4CLASS="example"><ACLASS="title"NAME="ch16-19570">Example 16.2: cmd3sel</A></H4><PRECLASS="programlisting">#!/usr/bin/perl# <ACLASS="indexterm"NAME="ch16-idx-1000006344-0"></A>cmd3sel - control all three of kids in, out, and error.use IPC::Open3;use IO::Select;$cmd = &quot;grep vt33 /none/such - /etc/termcap&quot;;$pid = open3(*CMD_IN, *CMD_OUT, *CMD_ERR, $cmd);$SIG{CHLD} = sub {    print &quot;REAPER: status $? on $pid\n&quot; if waitpid($pid, 0) &gt; 0};print CMD_IN &quot;This line has a vt33 lurking in it\n&quot;;close(CMD_IN);$selector = IO::Select-&gt;new();$selector-&gt;add(*CMD_ERR, *CMD_OUT);while (@ready = $selector-&gt;can_read) {    foreach $fh (@ready) {        if (fileno($fh) == fileno(CMD_ERR)) {print &quot;STDERR: &quot;, scalar &lt;CMD_ERR&gt;}        else                                {print &quot;STDOUT: &quot;, scalar &lt;CMD_OUT&gt;}        $selector-&gt;remove($fh) if eof($fh);    }}close(CMD_OUT);close(CMD_ERR);</PRE></DIV><PCLASS="para">We sent only a short line as input, then closed the handle. This avoids the deadlock situation of two processes each waiting for the other to write <ACLASS="indexterm"NAME="ch16-idx-1000006336-0"></A><ACLASS="indexterm"NAME="ch16-idx-1000006336-1"></A><ACLASS="indexterm"NAME="ch16-idx-1000006336-2"></A><ACLASS="indexterm"NAME="ch16-idx-1000006336-3"></A>something.<ACLASS="indexterm"NAME="ch16-idx-1000006329-0"></A><ACLASS="indexterm"NAME="ch16-idx-1000006329-1"></A><ACLASS="indexterm"NAME="ch16-idx-1000006329-2"></A><ACLASS="indexterm"NAME="ch16-idx-1000006329-3"></A></P></DIV><DIVCLASS="sect2"><H3CLASS="sect2"><ACLASS="title"NAME="ch16-pgfId-1594">See Also</A></H3><PCLASS="para">The documentation for the standard IO::Select, IPC::Open2, and IPC::Open3 modules; the <ACLASS="olink"HREF="../prog/ch03_005.htm"><CODECLASS="literal">alarm</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> or <ICLASS="filename">perlfunc </I>(1); <ACLASS="xref"HREF="ch16_09.htm"TITLE="Controlling Input and Output of Another Program">Recipe 16.8</A>; <ACLASS="xref"HREF="ch16_16.htm"TITLE="Installing a Signal Handler">Recipe 16.15</A>; <ACLASS="xref"HREF="ch16_17.htm"TITLE="Temporarily Overriding a Signal Handler">Recipe 16.16</A></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_09.htm"TITLE="16.8. Controlling Input and Output of Another Program"><IMGSRC="../gifs/txtpreva.gif"ALT="Previous: 16.8. Controlling Input and Output of Another Program"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_11.htm"TITLE="16.10. Communicating Between Related Processes"><IMGSRC="../gifs/txtnexta.gif"ALT="Next: 16.10. Communicating Between Related Processes"BORDER="0"></A></TD></TR><TR><TDALIGN="LEFT"VALIGN="TOP"WIDTH="228">16.8. Controlling Input and Output of Another Program</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.10. Communicating Between Related Processes</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 &copy; 2002</a> O'Reilly &amp; 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 + =
减小字号Ctrl + -
显示快捷键?