tags:

views:

92

answers:

1

For some reason Perl keeps caching the directory entries I'm trying to read using readdir:

opendir(SNIPPETS, $dir_snippets); # or die...
while ( my $snippet = readdir(SNIPPETS) )
 { print ">>>".$snippet."\n"; }
closedir(SNIPPETS);

Since my directory contains two files, test.pl and test.man, I'm expecting the following output:

.
..
test.pl
test.man

Unfortunately Perl returns a lot of files that have since vanished, for example because I tried to rename them. After I move test.pl to test.yeah Perl will return the following list:

.
..
test.pl
test.yeah
test.man

What's the reason for this strange behaviour? The documentation for opendir, readdir and closedir doesn't mention some sort of caching mechanism. "ls -l" clearly lists only two files.

+6  A: 

The result of "opendir" seems to be a list of files which were in the directory at the time it was called. If you alter the directory you need to call "rewinddir":

my $dir_snippets = "/tmp/fruit";
system ("rm -rf $dir_snippets");
mkdir $dir_snippets or die $!;
my $banana = "$dir_snippets/banana";
system ("touch $banana");
opendir(SNIPPETS, $dir_snippets); # or die...
while ( my $snippet = readdir(SNIPPETS) ) {
    if (-f $banana) {
        unlink $banana;
        rewinddir SNIPPETS;
    }
    print ">>>".$snippet."\n";
}
closedir(SNIPPETS);

Gives you

>>>.
>>>.
>>>..

Without the rewinddir you get

>>>.
>>>..
>>>banana

Just testing with C, I get the same thing:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>

int main ()
{
    DIR * fruit;
    struct dirent * file;

    fruit = opendir ("/tmp/fruit");
    if (! fruit) {
        fprintf (stderr, "opendir failed: %s\n", strerror (errno));
        exit (EXIT_FAILURE);
    }
    while (file = readdir (fruit)) {
        unlink ("/tmp/fruit/banana");
        printf (">>> %s\n", file->d_name);
    }
    closedir (fruit);
}

Gives the following (after creating the file "banana" with "touch"):

$ ./a.out 
>>> .
>>> ..
>>> banana
$ ./a.out 
>>> .
>>> ..
Snake Plissken
Whoa, works like charm now, thanks! I called opendir() right on top of the script expecting readdir() to do the actual, well, reading.tl;dr: Either call opendir right before readdir or use rewinddir if you expect changes in the directory you're reading from.
Frank Straetz