Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change

Unable to reuse STDIN

by fireblood (Scribe)
on Jul 04, 2016 at 02:04 UTC ( #1167099=perlquestion: print w/replies, xml ) Need Help??

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

Hello, I'm trying to use STDIN first to read from the output of the previous process and then to read from the terminal. According to I should be able to do this:

"The <> symbol will return undef for end-of-file only once. If you call it again after this, it will assume you are processing another @ARGV list, and if you haven't set @ARGV , will read input from STDIN."

That's what I need! My simplified program, called pgm, is the following:

#!/opt/local/tools/bin/perl use 5.24.0; use strict; use warnings; my $response; unless (-t STDIN) { while (<STDIN>) { chomp; print "input line is $_\n"; } } print "Now enter something: "; $response = <>; print "You entered -->", $response, "<--\n";

and I invoke it as follows:

echo Hello | pgm

But it doesn't wait for anything to be entered at the <>:

echo Hello | pgm input line is Hello Use of uninitialized value $response in print at /userdata/cfor/utils/ +worklib/pgm line 25. Now enter something: You entered --><--

Wouldn't the while (<STDIN>) end because it encountered EOF, after which it should then read input from STDIN?

Replies are listed 'Best First'.
Re: Unable to reuse STDIN
by GrandFather (Saint) on Jul 04, 2016 at 02:34 UTC

    The Hello | is providing stuff on STDIN. You don't have anything in @ARGV because you have no command line arguments to pgm. Even if you did provide command line parameters things still wouldn't go your way - a file provided on the command line would be processed after the while loop and would provide the responses you hope to get from the user.

    What is the big picture? What do you really want to achieve?

    Premature optimization is the root of all job security
Re: Unable to reuse STDIN
by BillKSmith (Monsignor) on Jul 04, 2016 at 04:02 UTC
    I think that you have confused STDIN with the keyboard that it is usually connected to. In your script, STDIN is open to the pipe. The end-of-file does not change that. You probably have to reopen STDIN to the keyboard yourself. I have not been able to do this in windows. I cannot make a reasonable guess for how to do it on any other system.
Re: Unable to reuse STDIN
by haukex (Archbishop) on Jul 04, 2016 at 07:47 UTC

    Hi fireblood,

    There's a few issues here:

    First, the piece of documentation you quoted refers to re-using <>, but your program doesn't do that, it only uses <> once. Note that the documentation you quoted is referring specifically to the magic <> operator with no filehandle, not to the <...> operator in general.

    Second, I think BillKSmith is right: in your example, your STDIN is being fed from echo's output by the shell, i.e. the redirection of STDIN is external to your program.

    Here's something that works for me (Linux):

    use Term::ReadLine; my $term = Term::ReadLine->new; while(<>) { chomp; print "Got line: \"$_\"\n"; } my $in = $term->readline("Input: "); print "Got input: \"$in\"\n";

    Example session (I'm typing "test" at the prompt):

    $ echo -e "Hello\nWorld" | perl Got line: "Hello" Got line: "World" Input: test Got input: "test"

    But I'd also ask what the bigger picture is? I think you'd have more choices as to how to handle your input if you didn't rely on the shell's pipe, instead you could have the shell write the output of the previous program to a file and have your script read that, or have your script run the command as a child and read its output (there are many modules to help you with that - I listed some of them here).

    Hope this helps,
    -- Hauke D

Re: Unable to reuse STDIN
by fireblood (Scribe) on Jul 05, 2016 at 21:09 UTC

    Hi all, thanks for your replies, I've checked out the Term::ReadLine module and am now close to a solution (still haven't figured out how to suppress the termcap attributes in the prompt yet). Regarding the inquiry from two experts regarding what the big picture is -- what I'm doing is writing a script that I call dir_zip that runs in my department's UNIX (Solaris) environment that compresses old project directories. For each directory, I first use pax to serializes its contents into a single file and then use bzip2 to compress that file. I allow the user to specify any number of target directories on the command line, using any of several different syntaxes:

    Usage: The dir_zip script may be invoked using any of the following f +orms: dir_zip (list of directories to be zipped) dir_zip < (name of file containing list of directories to be z +ipped) echo (list of directories to be zipped) | dir_zip cat (name of file containing list of directories to be zipped) + | dir_zip The first and the third form may use wildcard characters, e.g. dir_zip + p08* You can use a file to hold a list of only the directories for which yo +u have update access.

    Depending on which form the user chooses I collect the list of target directories either through @ARGV or <STDIN>. Then I loop through them doing a series of checks on each one, e.g. that the user has read permission on the directory, then that the user has write permission on the directory, and finally that there are no nested subdirectories at any level down under the target directory for which the user does not have read access. If all of these checks pass, then I check whether the user is the owner of the target directory. If not, then I add the name of the target directory to an array of directories that the user does own. After the loop has completed examining the list of target directories, I then list the directory or directories that the user does not own and say that if the process continues, the user will become the owner of those directories. Then I prompt for whether or not the user wants to continue.

    I could solve the problem by not letting the user identify the target directories through <STDIN> at the beginning, but there are reasons for allowing it. My first version of the script let the user specify the target directory names only via @ARGV, e.g. "dir_zip p08*". But then I realized that in some cases p08* globbed into a list of directories of which one or two might be unreadable by the user. So I updated the script to allow "ls p08* | grep -v p08_unreadable | dir_zip" to enable initial generation of the list from ls and then filtering out of selected individual entries. Being able to do the latter requires pipelining, so I didn't see a way to avoid the use of <STDIN> at the beginning of the script. So then, when <STDIN> or <> needed to be used later to elicit confirmation from the user that it is okay to make him the owner of directories that he currently doesn't own, that's where I ran into the problem.

    So it looks as if using the Term::ReadLine module allows me to be able to read the initial directory list from <STDIN> if the user chooses that route, and then still to be able to interact with the user later on.

    I greatly appreciate the guidance, thanks much!

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1167099]
Approved by GrandFather
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others cooling their heels in the Monastery: (3)
As of 2022-12-03 10:27 GMT
Find Nodes?
    Voting Booth?

    No recent polls found