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

I want to run arbitrary system commands and process each line of output as it's generated. I have some sample code that shows how I'm trying to accomplish this using IO::Pipe. It works fine for non-interactive commands, however input prompts are not displayed until after input has been recieved. The result is that the user cannot see the prompt and does not know what they are being prompted for, or even that they are being prompted. It's as if the command writes its prompt to the pipe and then blocks for input before the prompt can be read and subsequently written to stdout.

From what I understand, calling IO::Pipe::reader with an argument causes a child process to be forked and the command to be exec'd within that child process. So even if the child process is blocking for input, that shouldn't block the parent process - unless this is a limitation of fork emulation on Win32.

So my questions are:

  1. What is causing this problem?
  2. How can I overcome this problem using my current method?
  3. Is there a better approach to my objectives?
use IO::Pipe; use warnings; use strict; my $cmd = shift || "pause"; my $pipe = new IO::Pipe; $pipe->reader($cmd); print while (<$pipe>); __END__

Replies are listed 'Best First'.
Re: Interactive IO with IO::Pipe on Win32
by BrowserUk (Patriarch) on Feb 14, 2006 at 02:43 UTC
    it works fine for non-interactive commands, however input prompts are not displayed until after input has been received. The result is that the user cannot see the prompt and does not know what they are being prompted for, or even that they are being prompted.

    Maybe I'm misunderstanding something here but...of course the user doesn't see the prompts, how could they?

    The program $cmd in your example will write it's prompts to stdout, and $pipe->reader( $cmd ) runs that command, redirecting it's stdout to a pipe, so that your program can read it.

    Just as you don't see the output from dir when you do dir >file, so you are not going to see the prompt from (say) pause when you do pause > junk because it ends up in the file.

    C:\test>type junk Press any key to continue . . .

    Same thing with your pipe, any prompts from the command will end up going via the pipe into your program. Unless the command was printing to the console via some other filehandle (eg. stderr) or using direct console IO.

    Here is a way that might work for you. If you don't have a copy of the tee utility, you can get a Win32 version as a part of UnxTools. You then issue your command as

    $pipe->reader( 'yourprog arg arg | tee CON' );

    Which will allow the user to see the prompts and respond to them

    C:\test>perl -MIO::Pipe -we"$p=IO::Pipe->reader('(pause & dir z)|tee C +ON');@i=<$p>;print qq[Got '$_']for @i" Press any key to continue . . . Volume in drive C has no label. Volume Serial Number is BCCA-B4CC Directory of C:\test File Not Found Got 'Press any key to continue . . . 'Got ' Volume in drive C has no label. 'Got ' Volume Serial Number is BCCA-B4CC 'Got ' 'Got ' Directory of C:\test 'Got ' '

    with the caveats that

    1. your program will also receive a copy of the prompts in it's input which you could probably filter out;
    2. The user will also see all the output from the command echo'd on the screen which may or may not be a problem.

    What it comes down to is that there is no way for the pipe to know which output (to stdout) from the command is intended for interaction with the user, and what is a part of the final output you wish to capture. This is not a win32 limitation (of it's forking or anything else), you will get the same results on *nix also.

    Finally, you may or may not know that IO::File->reader is effectively the same as doing

    $pid=open CMD, "$cmd |"; @input = <CMD>;

    eg.

    C:\test>perl -e"$pid=open CMD,q[(pause & dir z)|tee CON |];@i=<CMD>;pr +int qq[Got '$_']for @i" Press any key to continue . . . Volume in drive C has no label. Volume Serial Number is BCCA-B4CC Directory of C:\test File Not Found Got 'Press any key to continue . . . 'Got ' Volume in drive C has no label. 'Got ' Volume Serial Number is BCCA-B4CC 'Got ' 'Got ' Directory of C:\test 'Got ' '

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      Hi BrowserUK,

      Thanks for your response. There does seem to be a misunderstanding as I'm having no trouble forwarding output from the command to the console. The only time I have a problem is when the command prompts for input. The prompt will appear on the console, but only after the input has been entered.

        forwarding output from the command to the console.

        By "forwarding" I assume you mean printing the input you receive?

        The prompt will appear on the console, but only after the input has been entered.

        That because you can't print it until you have read it, and you won't be able to read it until readline (in the guise of the diamond operator <$fh>), returns, which it won't until it see a newline.

        As many prompts are printed without a newline so that the users input goes on the same line as the prompt, the text of the prompt will not be dispatched into the pipe until a newline is seen, and that won't happen until the user enters one.

        Given that you appear to be printing everything received from the command, the tee trick I offered would probably work well for you. You just allow tee to do the forwarding and you just gather the output within your program for whatever processing you need to do, rather than echoing it yourself. The only extra step required is to filter the prompt and user input from the datastream.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Interactive IO with IO::Pipe on Win32
by ikegami (Patriarch) on Feb 13, 2006 at 23:31 UTC
    I don't know the answer — it sounds like a buffering issue, but I can't solve it — but Win32's fork emulation is not involved. IO::Pipe uses system 1, instead of fork on Win32.