Reading from stdin then launching a program that reads from stdin strange behaviour

Discussion in 'Perl Misc' started by Stefano Sabatini, Jul 27, 2007.

  1. Hi all perlers,

    I'm writing a perl script which reads from stdin, then launch an
    interactive session of gnuplot. I'm on a gnu-linux system, perl 5.8.

    The simplified code looks like this:

    #! /usr/bin/perl

    # reads from stdin, then lauch interactively gnuplot

    open(FH, "/dev/stdin");
    # filter stdin
    while (<FH>) {
    ;
    }
    close(FH);

    print "Launching gnuplot...\n";
    # and now launch a gnuplot interactive session
    system "gnuplot -";

    If I feed the script with a file, like this:
    cat file.txt | perl gnuplot-launcher.pl

    gnuplot exits immediately from the interactive session.

    If I filter a regular file (like "file.txt") or type interactively on
    stdin like this:
    cat - | gnuplot-launcher.pl
    I'm writing
    interactively
    on
    stdin
    ^D

    then the gnuplot interactive mode seems to work.

    All I want to do is to be able to access to an interactive session of
    gnuplot from perl, but I'm evidently missing something.

    Many thanks in advance.
    Cheers.
     
    Stefano Sabatini, Jul 27, 2007
    #1
    1. Advertisements

  2. Stefano Sabatini

    Mumia W. Guest

    You don't provide enough detail about what you're trying to do, but I
    suspect that you can just use 'open' to launch gnuplot:

    open (GNUPLOT, "|-", "gnuplot") or die("blah blah");
    print GNUPLOT "plot blah blah blah...\n";
    close GNUPLOT or die("blah blah");

    Read up on the open command: perldoc -f open. If you need more
    complicated interactions with gnuplot (not likely), you might consider
    using Expect.pm.
     
    Mumia W., Jul 27, 2007
    #2
    1. Advertisements

  3. Stefano Sabatini

    Klaus Guest

    So far so good, but at this point, after closing FH, you should also
    close STDIN:

    close STDIN;

    Now, before launching an interactive application, you want to re-open
    STDIN:

    open STDIN, '<&1' or die "Error open STDIN: $!";
     
    Klaus, Jul 28, 2007
    #3
  4. Thank you guys, you saved my day!

    Here it is the complete solution.

    -----------------------------
    while (<>) {
    ;
    }
    close (STDIN);
    open STDIN, '<&1' or die "Error open STDIN: $!";
    system("gnuplot", "-");
     
    Stefano Sabatini, Jul 28, 2007
    #4
  5. I don't see how this can work. It's completely equivalent to the
    previous example, as far as gnuplot-launcher.pl is concerned:

    cat reads from stdin until it a read returns 0 bytes (this happens on
    EOF on a file, or when the user types ^D on a terminal). It copies all
    input to the pipe, then exits.

    gnuplot-launcher.pl reads from the pipe until the write-end of the pipe
    is closed (i.e. cat has exited). Then it launches "gnuplot -", which
    will still try to read from a pipe with no writer, so it gets EOF
    immediately and exits.

    Now invoking gnuplot-launcher.pl with stdin from a terminal is a bit
    different. In this case it will read until the user presses ^D, then
    launch gnuplot, which will again run until the user presses ^D. This is
    because a terminal doesn't really have an "end of file", it can only
    return 0 bytes to a read which is interpreted as end of file by most
    programs - and it can do that as often as necessary, of course.
    open(STDIN, '<', '/dev/tty') or die "cannot open /dev/tty: $!";

    should work if the process has a controlling tty (otherwise it gets
    tricky: You could start gnuplot in an xterm if $DISPLAY is set).

    hp
     
    Peter J. Holzer, Jul 28, 2007
    #5
  6. You are duplicating STDIN from STDOUT here. It is not guaranteed that
    STDOUT is readable. Consider:

    ../gnuplot-launcher < foo > bar

    hp
     
    Peter J. Holzer, Jul 28, 2007
    #6
  7. Yes you're right, indeed I tried it again and it didn't work, sorry
    for my error.
    Thank you Peter for the indepth analysis, using /dev/tty seems like a
    good idea.

    I think the key to understand the problem is to understand how a perl
    script interprets its input.

    In the case:
    cat - | filter.pl

    (or more in general in the case prog | filter.pl) the input is the
    pipe output of the program which feeds the input pipe, when it sends
    EOF then the STDIN filehandler is closed, and other reads from it are
    doomed to get an immediate EOF and exit.

    In the case:
    filter.pl

    STDIN is defined as a sort of alias to /dev/tty if the script is
    launched in a terminal, and the script reads from it up to the first
    ^D, but doesn't close it afterwhile, so successive reads will result
    in accepting the input from the terminal (interactively typed by the
    user).

    In the case of the gnuplot-launcher.pl the correct solution seems to
    me:

    # diamond operator: reads from input, which can be both the output end
    # of a pipe, a file specified in @ARGV, or /dev/tty
    while (<>) {
    print $_;
    }
    # at this point it could close STDIN, in the case it is the output end
    # of a pipe, and successive reads will fail.

    # we need to redefine STDIN as the terminal ouput, since
    # gnuplot inherits the STDIN filehandler from his parent,
    # the perl script
    open (STDIN, "</dev/tty") or die "Cannot open /dev/tty";
    system("gnuplot -") == 0 or die "Gnuplot error...: $!"

    This works in both the "prog | gnuplot-launcher.pl" and
    "gnuplot-launcher.pl" or "gnuplot-launcher.pl file" case.

    Hope my analisys is not too far from the truth.
    Thank you all for your help.

    Cheers!
     
    Stefano Sabatini, Jul 29, 2007
    #7
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.