tags:

views:

198

answers:

5

I usually use something like

my $dir="/path/to/dir";
opendir(DIR, $dir) or die "can't open $dir: $!";
my @files = readdir DIR;
closedir DIR;

or sometimes I use glob, but anyway, I always need to add a line or two to filter out . and .. which is quite annoying. How do you usually go about this common task?

+10  A: 
my @files = grep {!/^\./} readdir DIR;

This will exclude all the dotfiles as well, but that's usually What You Want.

Chris Jester-Young
+7  A: 

If some of the dotfiles are important,

my @files = grep !/^\.\.?$/, readdir DIR;

will only exclude . and ..

mobrule
Well, it might exclude "..\n", which is legal in Unix. Not that I wish that filename on anyone, but people do strange things to see if someone will ignore a file they've snuck into your directory. You can fix that with a \z instead of $ :)
brian d foy
+4  A: 

I will normally use the glob method:

for my $file (glob "$dir/*") {
    #do stuff with $file
}

This works fine unless the directory has lots of files in it. In those cases you have to switch back to readdir in a while loop (putting readdir in list context is just as bad as the glob):

open my $dh, $dir
    or die "could not open $dir: $!";

while (my $file = readdir $dh) {
    next if $file =~ /^[.]/;
    #do stuff with $file
}

Often though, if I am reading a bunch of files in a directory, I want to read them in a recursive manner. In those cases I use File::Find:

use File::Find;

find sub {
    return if /^[.]/;
    #do stuff with $_ or $File::Find::name
}, $dir;
Chas. Owens
The use of `[.]` is very curious---why do you prefer that to `\.`?
Chris Jester-Young
@Chris Jester-Young I just like it better. It seems less noisy (see [leaning toothpick syndrome](http://en.wikipedia.org/wiki/Leaning_toothpick_syndrome)). Also, and this is rationalizing, escapes in regex tend to mean "the next character does something special" (e.g. `\x{2e}`), but `\.` means it is just a period. Since most characters don't have special meaning inside a character classes, it makes a nice escape mechanism.
Chas. Owens
+2  A: 

When I just want the files (as opposed to directories), I use grep with a -f test:

my @files = grep { -f } readdir $dir;
Ether
This will only work if the directory handle is for the current directory.
Chris Jester-Young
@Chris: good point, one would need to chdir first, or construct an absolute path name in the grep with `File::Spec->catfile($dirname, $_)`.
Ether
+7  A: 

I often use File::Slurp. Benefits include: (1) Dies automatically if the directory does not exist. (2) Excludes . and .. by default. It's behavior is like readdir in that it does not return the full paths.

use File::Slurp qw(read_dir);

my $dir = '/path/to/dir';
my @contents = read_dir($dir);

Another useful module is File::Util, which provides many options when reading a directory. For example:

use File::Util;
my $dir = '/path/to/dir';
my $fu = File::Util->new;
my @contents = $fu->list_dir( $dir, '--with-paths', '--no-fsdots' );
FM
...which wasn't a requirement from the OP. He only wanted to filter out `.` and `..`.
CanSpice
Nice. Can I make it return the full paths (not the basenames)?
David B
`my @contents = map { "$dir/$_" } read_dir($dir);`
toolic
@David B You might also consider `File::Util`, as shown in the edited answer.
FM