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


S

Stefano Sabatini

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.
 
Ad

Advertisements

M

Mumia W.

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.

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.
 
K

Klaus

I'm writing a perl script which reads from stdin, then launch an
interactive session of gnuplot.
open(FH, "/dev/stdin");
# filter stdin
while (<FH>) {
;}

close(FH);

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: $!";
 
S

Stefano Sabatini

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: $!";

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", "-");
 
P

Peter J. Holzer

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. [...]
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.

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.
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.

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
 
P

Peter J. Holzer

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

open STDIN, '<&1' or die "Error open STDIN: $!";

You are duplicating STDIN from STDOUT here. It is not guaranteed that
STDOUT is readable. Consider:

../gnuplot-launcher < foo > bar

hp
 
Ad

Advertisements

S

Stefano Sabatini

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. [...]
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.

I don't see how this can work. It's completely equivalent to the
previous example, as far as gnuplot-launcher.pl is concerned:

Yes you're right, indeed I tried it again and it didn't work, sorry
for my error.
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).

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!
 

Top