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

Greetings.

I would like to understand when $_ gets a value assigned and when it doesn't. The question comes up in code like:

use strict; use warnings; while (1) { <STDIN>; m/./ and print "success.\n"; # ... (more code) ... }

If I run it and enter a non-empty line, I get:

Use of uninitialized value in pattern match (m//) at uninit.pl line 6, <STDIN> line 1.

My understanding is $_ does not have a value when the pattern match is done. The following does what was intended:

use strict; use warnings; while (<STDIN>) { m/./ and print "success.\n"; # ... (more code) ... }

What is the difference between the two snippets with regard to $_? Why does <STDIN> in the first snippet not give $_ a value? Or should I look at it from another angle somehow to understand it?

Replies are listed 'Best First'.
Re: <STDIN> not initializing $_
by pc88mxer (Vicar) on Apr 30, 2008 at 23:05 UTC
    Yes, while (<STDIN>) { ... } is special in that it:
    1. Reads a line from STDIN
    2. Assigns the line to $_
    3. Exits the loop if STDIN was at EOF before the read
    Invoking <STDIN> by itself just reads a line but doesn't automatically assign it to $_.

    Note that this is different from merely testing whether or not $_ is true as is shown by this example:

    $/ = "0"; while (<DATA>) { print "got $_ and it ", ($_ ? "is" : "is not"), " true\n"; } __END__ 00000
    which emits:
    got 0 and it is not true got 0 and it is not true ...
Re: <STDIN> not initializing $_
by oko1 (Deacon) on May 01, 2008 at 00:41 UTC

    The part that you may be missing is that '$_' is not created by <STDIN> - it's created as a result of the 'while' loop, in the same way that it would be created if you'd used a 'for' loop.

    while (<STDIN>){ # Loops over <STDIN> as long as it returns ' +true' print; } while ($_ = <STDIN>){ # Exact equivalent of the above but "spelled + out" explicitly print $_; # Ditto for this line - exactly equvalent to + a simple 'print;' } for (1,2,3){ # Loop over specified list print; # Print out each item (which has been assign +ed to $_) in turn } for $_ (1,2,3){ # Exact equivalent of the above but "spelled + out" explicitly print $_; # Same story }

    In general, whenever you see a variable being omitted (e.g., 's/foo/bar/' instead of '$xyz =~ s/foo/bar/'), '$_' is being used. It's part of the standard Perl idiom, and you need to get familiar with it. At the very least, you'll need it to read other people's code.

    
    -- 
    Human history becomes more and more a race between education and catastrophe. -- HG Wells
    

      Not quite.
      while (<STDIN>)
      is equivalent to
      while ($_ = <STDIN>)
      but when spelled out explicitly, they're both
      while (defined($_ = <STDIN>))

      >perl -MO=Deparse -e"while (<STDIN>) {}" while (defined($_ = <STDIN>)) { (); } -e syntax OK >perl -MO=Deparse -e"while ($_ = <STDIN>) {}" while (defined($_ = <STDIN>)) { (); } -e syntax OK
      Note that the implicit "$_ = " in a while/until expression is only added for <>, readline, or glob operations.

      There's an implicit defined() also, as ikegami notes, but that's broader. It is added when the while/until condition is a scalar assignment (itself possibly implicit as noted above) from a <>, readline, glob, readdir, or each operation.

        Thank you all, monks, for your insights regarding my question. I'm currently in the process of reading "Programming Perl". Today I discovered the book explicitly explains <> and $_. It also confirms your answers, especially ysth's. The first part of `Line Input (Angle) Operator' in `Input Operators' in Chapter 2 covers the subject thoroughly. A short quote:
        If and only if the line input operator is the only thing inside the conditional of a while loop, the value is automatically assigned to the special variable $_.
        After that some useful examples follow.
        Update: Ignore this post. I read "and a" instead of "from a".

        It is added when the while/until condition is a scalar assignment

        As far as I can tell, that's not true. The following prints nothing:

        perl -le"while ($a=0) { print 'ysth is right'; last }"

        Tested with Perl 5.6.0, 5.6.1, 5.8.0, 5.8.8, 5.10.0.

Re: <STDIN> not initializing $_
by wade (Pilgrim) on Apr 30, 2008 at 23:22 UTC

    Yeah, and since $_ can get silently overwritten (especially as you add code and forget that you were counting on $_) I really hate to use it. Instead, I assign everything to my own variables:

    use strict; use warnings; while (1) { my $foo = <STDIN>; $foo =~ m/./ and print "success.\n"; # ... (more code) ... }

    I'd also tend not to use a while(1) loop but that's a whole other Oprah.

    --
    Wade
      > Yeah, and since $_ can get silently overwritten [...]
      

      Just about as silently as every other variable in Perl. No matter what your own preferences may be, '$_' is an important and integral part of Perl - and there's no reason to give misleading advice to someone who is trying to learn about it.

      > [...] I really hate to use it.
      

      Say 'goodbye' to grep and map, then. In fact, bid 'good night' to half the functionality of most Perl functions.

      
      -- 
      Human history becomes more and more a race between education and catastrophe. -- HG Wells
      
        I'm just saying that I like to assign values to variables explicitly when possible. I think it's clearer and a little less error-prone but that's just me.
        --
        Wade
Re: <STDIN> not initializing $_
by FunkyMonk (Bishop) on May 04, 2008 at 23:45 UTC
    I'm late coming in with this, sorry:(

    If while (<STDIN>) does something you don't expect, the Perl documentation should be your first port of call. You can...

    • google for perldoc while
    • search for while at http://perldoc.perl.org
    • Use perldoc perlsyn from the command line (unfortunately you need to know that while's described in perlsyn)
    • If you're using ActivePerl on windows, Perl's documentation can be accessed via Start -> Programs -> ActivePerl -> Documentation.

    Unless I state otherwise, all my code runs with strict and warnings