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: mike@software.com (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: <29l3l6$sku@rome.software.com>
References: <29j8i7INNpqn@dapsun.lif.icnet.uk>
NNTP-Posting-Host: rome.software.com
Keywords: STDIN STDOUT pipe

alex@bison.lif.icnet.uk (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
mike@software.com
============================================================================
#!/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;



