5

See also: How is "0" result from readdir not false in a while condition?. (Not a duplicate; just closely related.)


Where in the documentation does it say that while tests readdir for definedness? For instance, this code

#!/usr/bin/perl

use strict;
use warnings;

opendir my $dir, "/tmp" or die "$!";

while (my $file = readdir($dir)) {
        print "$file\n";
}
closedir $dir;

when run through B::Deparse yields:

use warnings;
use strict 'refs';
die "$!" unless opendir my $dir, '/tmp';
while (defined(my $file = readdir $dir)) {
    do {
        print "$file\n"
    };
}
z.pl syntax OK

I expect this behaviour, but I cannot find where it is specified. In the I/O Operators section of perlop it says

The following lines are equivalent:

     while (defined($_ = <STDIN>)) { print; }
     while ($_ = <STDIN>) { print; }
     while (<STDIN>) { print; }
     for (;<STDIN>;) { print; }
     print while defined($_ = <STDIN>);
     print while ($_ = <STDIN>);
     print while <STDIN>;

But there is no mention of readdir.

Community
  • 1
  • 1
Chas. Owens
  • 64,182
  • 22
  • 135
  • 226
  • There was a closely related question earlier today - asking about why the loop doesn't stop on a file called '0' and running on MacOS X -- was that you? Interesting discovery - it probably isn't documented except under the heading of Perl's general DWIM-iness. – Jonathan Leffler May 10 '09 at 04:04
  • @Jonathan Leffler I answered that question, but I am not satisfied with my answer, so I asked a more direct question in the hopes that it will jog someone's memory. I could swear I have seen it in the docs somewhere, but it may have been in Programming Perl or some other book. Or I could just be imagining it. If no one can find it in the docs I will probably submit a patch adding it to readdir's portion of perlfunc. I don't like relying on undocumented behaviour. – Chas. Owens May 10 '09 at 04:13
  • BTW, this came about because I noticed a whole load of samples in "Higher Order Perl" that didn't have the defined() test. I mailed the author saying "that's wrong". He mailed back saying "no it isn't". – Alnitak May 11 '09 at 16:38
  • I would like to note that in [5.18](http://perldoc.perl.org/perl5180delta.html#Selected-Bug-Fixes) that was also [applied](http://perl5.git.perl.org/perl.git/commit/8ae39f603f0f5778c160e18e08df60affbd5a620) to [`each`](http://perldoc.perl.org/functions/each.html); which wouldn't make much sense to document in that [section](http://perldoc.perl.org/perlop.html#I%2fO-Operators) of [perlop](http://perldoc.perl.org/perlop.html). – Brad Gilbert Jan 30 '14 at 16:56

2 Answers2

6

You're quite right about it being undocumented. I've looked rather hard, and I can't find any reference to it being special either. It is special, as you've discovered, and as demonstrated by:

$ perl -MO=Deparse \
       -E'opendir(my $dir, "."); while($_ = readdir($dir)) { say; }'

BEGIN {
    $^H{'feature_say'} = q(1);
    $^H{'feature_state'} = q(1);
    $^H{'feature_switch'} = q(1);
}
opendir my $dir, '.';
while (defined($_ = readdir $dir)) {
    say $_;
}
-e syntax OK

Looking through the source, Perl_newWHILEOP in op.c specifically has tests for readdir, glob, readline and each... Hmm, let's do some digging, and see when readdir was added.

A bit of digging with git reveals that it's been that way since at least 1998, with Gurusamy Sarathy making the relevant change in commit 55d729e4. While I haven't gone digging to see which releases that's gone into, I'd wager it would be at least 5.6.0 and above. I can't find any mention of it in the deltas.

It might be mentioned in the third edition camel book, but I haven't checked to find out.

I think that a patch here (or even just a note to p5p) would certainly be appreciated.

Paul

pjf
  • 5,993
  • 25
  • 42
  • Interesting, glob in while does get tested for definedness, but bsd_glob in while does not. Looks like it is time for a document patch. – Chas. Owens May 10 '09 at 19:30
  • see also the counter example in my question, where you can fool Perl's DWIM logic if the call to readdir() is not directly in the while() loop but in a function called in the while() test. – Alnitak May 11 '09 at 16:42
  • Actually the [`readdir`](http://perldoc.perl.org/functions/readdir.html) check was only [added in 2009 to 5.12 with commit 114c60ecb1f775ef1deb4fdc8fb8e3a6f343d13d](http://perl5.git.perl.org/perl.git/commit/114c60ecb1f775ef1deb4fdc8fb8e3a6f343d13d). It was extended to [`each`](http://perldoc.perl.org/functions/each.html) [in 2012 to 5.18 with commit 8ae39f603f0f5778c160e18e08df60affbd5a620](http://perl5.git.perl.org/perl.git/commit/8ae39f603f0f5778c160e18e08df60affbd5a620). – Brad Gilbert Jan 30 '14 at 17:03
2

I'm sorry that I didn't think to update the pages that you expected to find this information.

I did add a note to the readdir section of perlfunc though.

As of Perl 5.12 you can use a bare readdir in a while loop, which will set $_ on every iteration.

opendir(my $dh, $some_dir) || die;
while(readdir $dh) {
    print "$some_dir/$_\n";
}
closedir $dh;

It doesn't explicitly say that it checks for definedness though.

( I would like to note that adding this feature was my first patch to the Perl core. )

Brad Gilbert
  • 33,846
  • 11
  • 78
  • 129