Article 6829 of comp.lang.perl:
Xref: feenix.metronet.com comp.lang.perl:6829
Path: feenix.metronet.com!news.ecn.bgu.edu!usenet.ins.cwru.edu!howland.reston.ans.net!europa.eng.gtefsd.com!uunet!olivea!pagesat!news.cerf.net!software.com!not-for-mail
From: [email protected] (Michael D'Errico)
Newsgroups: comp.lang.perl
Subject: Connecting to STDIN/STDOUT of another program (was "Redirecting I/O")
Date: 14 Oct 1993 19:58:46 -0700
Organization: Software Now, Santa Barbara, CA
Lines: 123
Distribution: world
Message-ID: <[email protected]>
References: <[email protected]>
NNTP-Posting-Host: rome.software.com
Keywords: STDIN STDOUT pipe

[email protected] (Alex Whittaker - BIU) writes:

> The good book
> (Nutshell) says "You may not have an open command that pipes both in and
> out, though it's easy to build one using the pipe and fork commands).
> Well, I guess you know why I am here now, how do I both read and write to
> a program,

Below is a script that does this.  It creates two pipes for communication
between the Perl script and the external program, then forks and execs the
external program with the pipes attached to STDIN/STDOUT.  The perl script
also creates the filehandles KEYBOARD and TERMINAL which do the obvious.

You can then issue commands to the program via STDOUT, and read the responses
from STDIN.  User inputs are read from KEYBOARD and outputs are displayed
to TERMINAL.

Here is a simple diagram of the inter-process communication:
                                                            ______  \||/_
 ______  STDOUT           STDIN  ______  TERMINAL          |I coul|  oo \
|      |---------[pipe]------>>>|      |---------------->>>|dn't r|   L_
| Your |                        | Perl |                   |esist!|    \/
| Prog | STDIN           STDOUT |      | KEYBOARD          ________    |
|______|<<<------[pipe]---------|______|<<<---------------[========]  /
                                                                 ---OOO-
                                                                |ooooooo|
> and what are the dangers darkly hinted at.

You may need to setup the read to be non-blocking, or your script may hang
indefinitely.  Look into using sysread if you have problems with buffering.

Michael D'Errico
[email protected]
============================================================================
#!/usr/bin/perl

##### program you want to run
$program_name = "command name goes here";
$program_args = "command-line arguments go here";

#####################################################################
##### only need to modify below where it says the script starts #####
#####################################################################

##### create pipes to handle communication
pipe (FROM_PERL, TO_PROGRAM);
pipe (FROM_PROGRAM, TO_PERL);

##### create a child process
$pid = fork;
die "Couldn't fork: $!" unless defined $pid;

##### child process becomes the program
if ($pid == 0)  {

   ##### attach standard input/output/error to the pipes
   close  STDIN;
   open  (STDIN,  '<&FROM_PERL') || die ("open: $!");

   close  STDOUT;
   open  (STDOUT, '>&TO_PERL')   || die ("open: $!");

   close  STDERR;
   open  (STDERR, '>&STDOUT')    || die;

   ##### close unused parts of pipes
   close FROM_PROGRAM;
   close TO_PROGRAM;

   ##### unbuffer the outputs
   select STDERR; $| = 1;
   select STDOUT; $| = 1;

   ##### execute the program
   exec $program_name split (' ', $program_name, $program_args);

   ##### shouldn't get here!!!
   die;
}

##### parent process is the perl script
open (TERMINAL, '>&STDOUT');
open (KEYBOARD, '<&STDIN');

close STDIN;
open (STDIN,    '<&FROM_PROGRAM') || die ("open: $!");

close STDOUT;
open (STDOUT,   '>&TO_PROGRAM')   || die ("open: $!");

close FROM_PERL;
close TO_PERL;

##### unbuffer all the outputs
select TERMINAL; $| = 1;
select STDERR;   $| = 1;
select STDOUT;   $| = 1;

#####################################################################
#######################  Script Starts Here  ########################
#####################################################################

##### This is just an example of what you can do....

print TERMINAL "Enter a command: ";

while (<KEYBOARD>)  {
   last if /quit/;     # stop when 'quit' is entered

   print;              # send keyboard input to the program

   $reply = <STDIN>;   # get (first line of) reply from program
   chop $reply;        # remove trailing "\n" character

   print TERMINAL "Response: \"$reply\"\n\n";
   print TERMINAL "Enter a command: ";
}

print TERMINAL "\nDone.\n";

kill $pid;
exit;