16.9. Controlling the Input, Output, and Error of Another ProgramProblemYou want full control over a command's input, output, and error streams. SolutionCarefully use the standard IPC::Open3 module, possibly in conjunction with the IO::Select module. (IO::Select is new as of the 5.004 distribution.) DiscussionIf 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: @all = `($cmd | sed -e 's/^/stdout: /' ) 2>&1`; for (@all) { push @{ s/stdout: // ? \@outlines : \@errlines }, $_ } print "STDOUT:\n", @outlines, "\n"; print "STDERR:\n", @errlines, "\n"; If you don't have sed on your system, you'll find that for simple cases like this, perl -pe works just as well as sed -e .
However, that's not really simultaneous processing. All we're doing is marking STDOUT lines with You can use the standard IPC::Open3 module for this. Mysteriously, the argument order is different for IPC::Open3 than for IPC::Open2. open3(*WRITEHANDLE, *READHANDLE, *ERRHANDLE, "program to run");
Using this has even
more
potential for chaos than using
You can avoid this deadlock by mimicking 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 = <HIS_OUT>; # read till EOF @errlines = <HIS_ERR>; # XXX: block potential if massive print "STDOUT:\n", @outlines, "\n"; print "STDERR:\n", @errlines, "\n";
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
We use the
IO::Select module (you could also do this with the built-in function 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 Example 16.2 .) Example 16.2: cmd3sel#!/usr/bin/perl # cmd3sel - control all three of kids in, out, and error. use IPC::Open3; use IO::Select; $cmd = "grep vt33 /none/such - /etc/termcap"; $pid = open3(*CMD_IN, *CMD_OUT, *CMD_ERR, $cmd); $SIG{CHLD} = sub { print "REAPER: status $? on $pid\n" if waitpid($pid, 0) > 0 }; print CMD_IN "This line has a vt33 lurking in it\n"; close(CMD_IN); $selector = IO::Select->new(); $selector->add(*CMD_ERR, *CMD_OUT); while (@ready = $selector->can_read) { foreach $fh (@ready) { if (fileno($fh) == fileno(CMD_ERR)) {print "STDERR: ", scalar <CMD_ERR>} else {print "STDOUT: ", scalar <CMD_OUT>} $selector->remove($fh) if eof($fh); } } close(CMD_OUT); close(CMD_ERR); 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 something. See Also
The documentation for the standard IO::Select, IPC::Open2, and IPC::Open3 modules; the
Copyright © 2001 O'Reilly & Associates. All rights reserved. |
|