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

Why does perl take a file name provided on the command line and open it for use with standard input? How should I prevent that?

I'm working on a program that takes a couple of files as command line arguments. At some point in the program, I want to prompt the user for an action to take.

The lines that accept the user's response look something like this:

print "Remove '$origin'? ([Y]es, [N], A(ll), ne[V]er; default: N) "; my $answer = <>; print "answer: |$answer|\n" if (DEBUG); chomp($answer); $answer = lc($answer); if ($answer eq ('y' or 'yes')) { unlink $origin; }

The problem is that <> is filled with the content of the first file on the command line. This is entirely unexpected. Why would the file be opened at all? I would have expected that filename to be treated only as a scalar value.

I confirmed this by adding while (<>) { print; } before I prompted for an answer.

To double-check, and eliminate problems caused by modules or other code, I put that line into a separate script and got the same results:

#!/usr/bin/perl -w use strict; while (<>) { print; }

Thank you.
--
Ghodmode
www.ghodmode.com/blog

Replies are listed 'Best First'.
Re: unexpected STDIN
by Corion (Patriarch) on Apr 30, 2011 at 06:17 UTC

    That's how <> and @ARGV are defined to work. This is mainly used by oneliners.

    If you really want to use <> instead of <STDIN>, you will need to empty @ARGV before asking a question.

      Thanks Corion. I don't have any particular preference for <> over <STDIN>. I mistakenly thought they were exactly the same. I blame tizag. I should have read I/O Operators at perldoc instead.

      Thank you.
      --
      Ghodmode
      www.ghodmode.com/blog
Re: unexpected STDIN
by CountZero (Bishop) on Apr 30, 2011 at 07:11 UTC
    You can change
    $answer = lc($answer); if ($answer eq ('y' or 'yes')) { unlink $origin; }
    by
    unlink $origin if $answer =~ m/^y(?:es)?$/i;
    which is not only shorter and more perlish, it evens works correctly!

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

      Thanks CountZero.

      My code was actually just a smaller part of the whole that I was using to isolate the problem I was experiencing. I wasn't having a problem with processing the response from the user, though.

      I like your solution, but it's a little hard to read and it doesn't work for my "neVer" answer.

      At the risk of being less perlish, I'm using a less concise version that will be a little easier (for me) to read and understand when I look at the code again in the future:

      if ($removeall == 1) { unlink $origin; } elsif ($removeall == 0) { print "Remove '$origin'? ([Y]es, [N]o, [A]ll, ne[V]er; default: N) + "; chomp(my $answer = lc(<STDIN>)); print "answer: |$answer|\n" if (DEBUG); if (($answer eq 'y') or ($answer eq 'yes')) { unlink $origin; } if (($answer eq 'a') or ($answer eq 'all')) { unlink $origin; $removeall = 1; } if ($answer eq ('v' or 'never')) { $removeall = -1; } }

      --
      Ghodmode
      www.ghodmode.com/blog
        As with most code, this can be improved further, yet still keep your own style. You are testing to see if $answer is 'y', and then testing to see if it is 'a'. It can't be both! No point in testing for 'a' or 'v' if it is 'y'. And what if the user mis-types, for example 'Q' (users never do what you want them to)?
        Also, you are not reporting if the unlink fails. autodie is an easy way of doing that without hacking your code too much:
        use strict; use warnings; use autodie; my $origin = 'somefile'; # inserted for testing my $removeall = 0; # inserted for testing if ($removeall == 1) { unlink $origin; } elsif ($removeall == 0) { print "Remove '$origin'? ([Y]es, [N]o, [A]ll, ne[V]er; default: N) + "; chomp(my $answer = lc(<STDIN>)); #print "answer: |$answer|\n" if (DEBUG); if (($answer eq 'y') or ($answer eq 'yes')) { unlink $origin; } elsif (($answer eq 'a') or ($answer eq 'all')) { unlink $origin; $removeall = 1; } elsif ($answer eq ('v' or 'never')) { $removeall = -1; } elsif ($answer ne '' and $answer ne 'n') { print "Invalid response: '$answer' (taken as N)\n"; } }
        and it doesn't work for my "neVer" answer
        Yes, it does: $answer =~ m/^v|(?:never)$/i

        If you are using a relatively modern Perl (5:10 or later) you can even use the Perl switch construct (which is called given ... when).

        use Modern::Perl; my $answer = <STDIN>; given ($answer) { when (/^y(?:es)?$/i) { say 'yes'; } when (/^no?$/i) { say 'no'; } when (/^a(?:ll)?$/i) { say 'all'; } when (/^v|(?:never)$/i) { say 'never'; } default { say 'default'; } }

        CountZero

        A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

Re: unexpected STDIN
by wind (Priest) on Apr 30, 2011 at 06:48 UTC

      Thanks wind, but it's not really clear what you mean. Could you be more specific?

      I think you might be referring to my placement of the parentheses and I do see the problem there. My code is actually evaluating ('y' or 'yes') first, then comparing the (boolean) result (true since 'y' is not an empty string) to the value of $answer.

      This is probably better: if (($answer eq 'y') or ($answer eq 'yes')){

      I caught that one on my own, but I hadn't gotten to that point yet when I asked the question. I wasn't even getting prompted for the info when I asked the question because it was using the file from the command line instead of <STDIN>

      The thread you linked to doesn't refer to order of precedence... Did you notice another problem that I haven't seen yet?

      Thank you.
      --
      Ghodmode
      www.ghodmode.com/blog
        My code is actually evaluating ('y' or 'yes') first, then comparing the (boolean) result (true since 'y' is not an empty string) to the value of $answer.

        If I correctly understand this statement, I would make the following small, but important, clarification:

        <quibble>
        The expression
            ('y' or 'yes')
        will always return 'y' specifically (rather than a 'canonical' true of 1) because the short-circuiting logical-or operator returns the value of the first operand that is true (or false if none is true). Thus
            (($answer eq 'y') or ($answer eq 'yes'))
        (fully parenthesized) is probably certainly better than
            ($answer eq ('y' or 'yes'))
        because the former will actually get around to evaluating  ($answer eq 'yes') someday if  $answer is not 'y'.
        </quibble>

        This may seem a mere, quibbling detail, but the Devil (and the 3 AM, tearing-your-hair-out debug session) is in the details.

        This is actually like “my first almost-bug,” in “my first program” which was a re-telling of the Corn/Fox/Chicken puzzle.   I had written statements (in BASIC) like this one:

        IF F AND C AND L = 1 THEN 80

        Although the program technically “worked” as written, my mentor at the time pointed out that it was not written the right way.   The computer interpreted the statement this way:

        IF (F) AND (C) AND (L = 1) THEN 80

        ... and it “worked” because a nonzero integer, in that particular (data-typeless...) BASIC dialect, was interpreted as “truth.”   But it did not express my thoughts at the time:

        IF (F = 1) AND (C = 1) AND (L = 1) THEN 80