gri6507 has asked for the wisdom of the Perl Monks concerning the following question:

Fellow monks,

I have the following dilemma. I have a program that prints out a few lines of text and then prompts the user for some input. I was asked to automate execution of this program, so, I came up with this:

use strict; use Socket; use IO::Handle; my $childPid; socketpair(CHILD, PARENT, AF_UNIX, SOCK_STREAM, PF_UNSPEC); CHILD->autoflush(1); PARENT->autoflush(1); die unless defined($childPid=fork()); if ($childPid){ close PARENT; print "about toread\n"; while (<CHILD>){ print "got $_"; last if (/InputPrompt/); } print CHILD "MyInput\n"; waitpid($childPid) exit; } close CHILD; open(STDOUT, ">&PARENT") or die "Can't dup stdout: $!\n"; open(STDIN, "<&PARENT") or die "Can't dup stdin: $!\n"; exec "myProgram" or die "Can't start my program: $!\n";

This works beautifully when "myProgram" is something like 'ls', so I know the communication works. However, when I run my program, the parent never gets anything, and hangs! It's as though the output of "myProgram" is not flushed when it gets to the input part.

Is that what's actually going on? If it is, how can I force the parent to flush "myProgram"'s output?

Update: myProgram is actually something similar to the following C code.

#include <stdlib.h> #include <stdio.h> int main(int argc, char* argv[]) { char b[32]; printf("Hello there\n"); printf("InputPrompt:\n "); gets(b); printf("Thanks, %s (%s)\n", b, argv[0]); }

Replies are listed 'Best First'.
Re: Communicating with unflushed child process
by tilly (Archbishop) on Nov 12, 2004 at 19:20 UTC
    The odds are that myProgram is detecting that it is not talking to a terminal and therefore is turning on buffering.

    One solution is to use Expect to do what you're trying above except that it gives the program you're trying to automate a terminal so that it won't decide to buffer.

    Another solution (better when doable, but isn't always doable) is to have the program that you're trying to automate be written as a library with a very small program that calls it. Then you can run the program that you have, and easily write other programs that do the same thing in an automated way.

    The big reason to prefer the latter approach is that it is hard with Expect to reliably take care of all of the unexpected outputs that a program can produce when it runs into error conditions. This leads to fragility that will come back and bite you when you least expect it to.

Re: Communicating with unflushed child process
by PreferredUserName (Pilgrim) on Nov 12, 2004 at 19:42 UTC
    If you control the subprogram, you could just flush the output after each write.

    The reason ls works is that it prints its output (buffered, like myProgram), but then exits, which flushes the buffer. If you were to run "perl -p -e s/cat/dog/' you would have the same buffering problems.

    Also, you should never use "gets()", since there's no way to stop it from writing past the 32rd byte of b[], which will kick your dog and steal your wife.

Re: Communicating with unflushed child process
by insaniac (Friar) on Nov 12, 2004 at 20:03 UTC
    UPDATE:
    the code doesn't work, the problem was that <CHILD> reads upon \n and with a prompt, that could be a problem.

    too hasty post:
    i've been messing around in your code and this seems to work:

    use Socket; use IO::Handle; use POSIX ":sys_wait_h"; my $childPid; socketpair(CHILD, PARENT, AF_UNIX, SOCK_STREAM, PF_UNSPEC); CHILD->autoflush(1); PARENT->autoflush(1); die "no child fork" unless defined($childPid=fork()); if ($childPid){ shutdown(PARENT,0); print "about to read\n"; while (<CHILD>){ last if (/Are you ready\? > /); print "got $_"; } print PARENT "yes\n"; print "we're ready\n"; } else { print "waiting for pid..\n"; } open(STDOUT, ">&",PARENT) or die "Can't dup stdout: $!\n"; open(STDIN, "<&",PARENT) or die "Can't dup stdin: $!\n"; exec "./fill.pl" or die "Can't start my program: $!\n"; shutdown(CHILD,1);
    and i used this as the fill.pl:
    #!/usr/bin/perl print <<END; bla bla bla bla bla bla lb alb blla bla bla bla bla bla END ASK: print "Are you ready? > \n"; $a=<STDIN>; if ($a =~ /yes/) {print "bla bla bla";exit 0;} else {goto ASK;}

    hope this helps

    --
    to ask a question is a moment of shame
    to remain ignorant is a lifelong shame
Re: Communicating with unflushed child process
by zentara (Cardinal) on Nov 13, 2004 at 13:04 UTC
    There is some interesting code in "perldoc -q filehandle" on how to detect if any data is sitting in a filehandle. Search for

    How can I tell whether there's a character waiting on a filehandle?

    It uses ioctl and has instructions on how to create .ph files from your system .h files.

    The basic idea is you can detect how much data is in a pipe and you can suck it out from the reader end.


    I'm not really a human, but I play one on earth. flash japh