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

If I allow my script to read from pipe or redirection
cat file_1 file_2|perlscript.pl or perlscript.pl <infile
how can I then at the same time check for user input?
It seems STDIN is used for both those things...

In humble search for enlightenment

Replies are listed 'Best First'.
Re: STDIN schizofrenia
by Zaxo (Archbishop) on Oct 13, 2003 at 00:41 UTC

    I'd skip the redirection. You can get the names of files from the command line in @ARGV and read them from the convenient default diamond <> (e.g. perl -e'print while <>' *.txt ). That leaves you free to use yout tty for STDIN.

    After Compline,
    Zaxo

Re: STDIN schizofrenia
by sgifford (Prior) on Oct 13, 2003 at 03:43 UTC
    Open /dev/tty, and use that for user input.
    #!/usr/bin/perl -w use strict; open(TTY,"+< /dev/tty") or die "Couldn't open tty: $!\n"; print TTY "Are you sure? "; if (my $l = <TTY> !~ /^y/i) { die "Cancelled by user.\n"; } while (<>) { print; }
Re: STDIN schizofrenia
by Anonymous Monk on Oct 13, 2003 at 12:00 UTC
    Thanks to all of you
    My purpose was to browse incomming records one at a time, and let the user decide if he wanted to see one more.
    I combined several of the ideas suggested here:
    • using <> for reading incomming records, so I don't have to care if data comes from pipe or redirection or files supplied on command line,
    • -t STDIN for determining if STDIN is a terminal,
    • open /dev/tty to get the user input (which was my original problem)
    • and Term::Readkey to avoid the need of hitting an extra RETURN.

    Finally the script turned out like this (only skeleton showned):
    #!/usr/bin/perl use strict; use warnings; use Term::ReadKey; # Is data comming? usage() unless ( ( ! -t STDIN ) || ((defined($ARGV[0])) && ( -f $ARGV[ +0] )) ); open(TTY,"+</dev/tty") or die "Couldn't open tty: $!\n"; # input record separator is \x1d $/ = "\x1D"; while ( my $rec = <>) { next unless defined $rec; show_formatted_record(); print TTY "\n\nOne more? y/n "; ReadMode('cbreak', *TTY); my $answer = ReadKey(0, *TTY); ReadMode('normal', *TTY); last if $answer !~ /^[yY]$/; } print "\n"; #---- functions: sub usage { print "usage: ...\n"; exit 1; } sub show_formatted_record { print "..."; }
    Thanks again,
    L
Re: STDIN schizofrenia
by rinceWind (Monsignor) on Oct 13, 2003 at 10:14 UTC
    If you want something portable, you are best off with Term::ReadLine for your prompted user input.

    You have also got Term::ReadKey if you want single keystroke responses.

    --
    I'm Not Just Another Perl Hacker
Re: STDIN schizofrenia
by etcshadow (Priest) on Oct 13, 2003 at 01:24 UTC
    You can read from STDERR. It's just as easy as <STDERR>.

    Correction:

    • You need to dupe STDERR as a readable file handle first, and this doesn't work in later versions of perl (5.8 and later won't allow you to dupe a write-only file-handle into a readable filehandle... although even by the 5.0 docs, it shouldn't be allowed (is my face red, or what))
      [me@host bin]$ perl -we 'open STDERRIN,"+<&STDERR" or die $!; my $line + = <STDERRIN>; print STDERRIN "YAY!!! $line";' asdfasf YAY!!! asdfasf [me@host bin]$

    If you are not redirecting, then STDIN, STDOUT and STDERR are really all references to the same thing, your terminal. You can see this by running (at least on linux):

    ls -l /proc/self/fd/
    as well as variations on the theme, like:
    ls -l /proc/self/fd/ < /dev/null ls -l /proc/self/fd/ | cat ls -l /proc/self/fd/ 2> /dev/null #redrect STDERR
    0 = STDIN, 1 = STDOUT, and 2 = STDERR.

    If you want to be clever, then you can try to detect which of STDIN, STDOUT and STDERR are actually terminals, by using the -t operator. Also, you could just reach around the standard IO channels and open a direct shot to the controlling tty of the process like:

    open TTY, ">/dev/tty" or die "Couldn't open /dev/tty $!";

    ------------
    :Wq
    Not an editor command: Wq
      You can read from STDERR. It's just as easy as <STDERR>.
      Really?
      $ perl -wle 'print while <STDERR>' Filehandle STDERR opened only for output at -e line 1. $

      How do you plan to read from STDERR, and how do you tell your terminal that what you type should be connected the the STDERR of the foreground job?

      Abigail

        ACK! My bad for posting untested code. You have to dupe STDERR to pull this off:
        open STDERRIN,"<&STDERR" or die "Could not dupe STDERR for input: $!\n +"; my $line = <STDERRIN>;
        While STDERR is a file-descriptor to your terminal (if you haven't redirected stdout), it is only an outbound channel... so you need to open an inbound channel to the same device if you want to read from it.

        ------------
        :Wq
        Not an editor command: Wq
Re: STDIN schizofrenia
by delirium (Chaplain) on Oct 13, 2003 at 11:51 UTC
    Pull the filenames out of @ARGV with shift, like so:

    perl -e '$fn=shift @ARGV;open FN, $fn;print while (<FN>);print "\nInpu +t :";print <STDIN>;' num.txt

    Which prints the contents of my "num.txt" file, then prompts for input:

    1481422100622192272001352141350602152800661492052301431991681632291972 +09 Input :test test