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

One of my program need to execute Unix command from time to time. If I use system(), it would spawn a new process each time, which costs a lot. My solution is to spawn a sh process through open2 or open3, and then simply communicate my Unix command to that sh process.

After the sh process receive my command, it may or may not spawn a new process, depending on the nature of the command, say I can save 50% (the number really depends on what commands I use) cost.

Part of my code read:
#!/usr/bin/perl use IPC::Open2; use strict; my ($reader, $writer); my $pid = open2($reader, $writer, "sh") || die "failed"; print $writer "history\n"; while (<$reader>) { print "[", $_, "]\n"; }
Now my problem is that it will not quit the while loop, as there is no eof coming in. So, how can I fix this? ( I also tried open3, the same thing)

By the way, there was a thread leading by help with system function, discussed about why can not call history sh command by using system() call, and no answer came up at that time. My solution actually resolves that problem.

Replies are listed 'Best First'.
(Expect) Re: question for open2, and by the way, resolves an old thread
by bbfu (Curate) on Jan 17, 2003 at 19:38 UTC

    The most robust solution would probably be to use Expect but that would probably require rewriting more code. You could just scan the output from <$reader> and look for the next prompt line. You'll have to know what the prompt looks like but you'll probably need to know that to use Expect anyway.

    bbfu
    Black flowers blossum
    Fearless on my breath

      That was what I thought, but interestingly, I didn't get the prompt back through $reader, Hm.. where did it go?

      If I can get it, the whole thing is resolved.

      Update:

      1. tye's suggestion definitely makes sense, as it might be just the prompt is not flushed yet. But I tried this:
        #!/usr/bin/perl use IPC::Open2; use strict; my ($reader, $writer); my $pid = open2($reader, $writer, "sh") || die "failed"; print $writer "history\n"; my $buffer; sysread($reader, $buffer, 4000); print "[", $buffer, "]\n"; print $writer "ls\n"; sysread($reader, $buffer, 4000); print "[", $buffer, "]\n";
        There is no prompt being printed between two prints. So it means/proves that the prompt is NOT THERE, doesn't matter what it is, but I do like tye's suggestion, it is 100% logical.

      2. I did a research, and someone said on internet that sh does not output prompt if it is not used interactively. This matches my result.

        But is there a switch to toggle this feature?

        Set your prompt to include a newline if you want to read it with <$reader>.

                        - tye
      Even better, you can set the prompt, by setting the PS1 variable. That way you'll be sure to know what it looks like, and you can set it to be something complicated, to reduce the risk of false positives.

      jdporter
      The 6th Rule of Perl Club is -- There is no Rule #6.

Re: question for open2, and by the way, resolves an old thread
by Helter (Chaplain) on Jan 17, 2003 at 21:39 UTC
    A little ugly, but you could append:
    ";echo "I am a unique string that will not appear otherwise""
    To your system call, then when you see this you can procede. That is if you cannot figure out how to get the prompt to print in non-interactive mode.

    Scanning the tcsh man page (we don't have sh here...at least not in my man path), there is a -i switch that says:
    -i The shell is interactive and prompts for its top-level input, even + if it appears to not be a terminal. Shells are interactive wi +thout this option if their inputs and outputs are terminals.

    Hope this helps!
Re: question for open2, and by the way, resolves an old thread
by gjb (Vicar) on Jan 17, 2003 at 20:44 UTC

    A bit nasty, but using

    print $writer "history; exit\n";
    will do what you want.

    Hope this helps, -gjb-