views:

3117

answers:

8

How do I get perl to read the contents of a given directory into an array?

Backticks can do it, but I know there is some method using 'scandir' or a similar term?

+9  A: 
opendir(D, "/path/to/directory") || die "Can't opedir: $!\n";
while (my $f = readdir(D)) {
  print "\$f = $f\n";
}
closedir(D);

EDIT: Oh, sorry, missed the "into an array" part:

my $d = shift;

opendir(D, "$d") || die "Can't opedir $d: $!\n";
my @list = readdir(D);
closedir(D);

foreach my $f (@list) {
  print "\$f = $f\n";
}

EDIT2: Most of the other answers are valid, but I wanted to comment on this answer specifically, in which this solution is offered:

opendir(DIR, $somedir) || die "can't opendir $somedir: $!";
@dots = grep { (!/^\./) && -f "$somedir/$_" } readdir(DIR);
closedir DIR;

First, to document what it's doing since the poster didn't, it's passing the returned list from readdir() through a grep() that only returns those values that are files (as opposed to dirs, devices, named pipes, etc) and that do not begin with a dot (which makes the list name @dots misleading, but that's due to the change he made when copying it over from the readdir() documentation). Since it limits the contents of the directory it returns I don't think it's technically a correct answer to this question, but it illustrates a common idiom used to filter filenames in perl and I thought it would be valuable to document. Another example seen a lot is:

@list = grep !/^\.\.?$/, readdir(D);

This snippet reads all contents from the dir handle D except '.' and '..', since those are very rarely desired to be used in the listing.

jj33
A: 

This will do it, in one line (note the '*' wildcard at the end)

@files = </path/to/directory/*>;
# To demonstrate:
print join(", ", @files);
rix0rrr
Can someone explain to me why this is "wrong"? Because it's clean and simple (contrary to some other solutions), but it has been downmodded without explanation.
rix0rrr
+1  A: 

Here's an example of recursing through a directory structure and copying files froma backup script I wrote.

sub copy_directory {
my ($source, $dest) = @_;
my $start = time;

# get the contents of the directory.
opendir(D, $source);
my @f = readdir(D);
closedir(D);

# recurse through the directory structure and copy files.
foreach my $file (@f) {
 # Setup the full path to the source and dest files.
    my $filename = $source . "\\" . $file;
    my $destfile = $dest . "\\" . $file;

    # get the file info for the 2 files.
    my $sourceInfo = stat( $filename );
    my $destInfo = stat( $destfile );

    # make sure the destinatin directory exists.
    mkdir( $dest, 0777 );

    if ($file eq '.' || $file eq '..') {
    } elsif (-d $filename) { # if it's a directory then recurse into it.
        #print "entering $filename\n";
        copy_directory($filename, $destfile); 
    } else { 
        # Only backup the file if it has been created/modified since the last backup 
        if( (not -e $destfile) || ($sourceInfo->mtime > $destInfo->mtime ) ) {
            #print $filename . " -> " . $destfile . "\n";
            copy( $filename, $destfile ) or print "Error copying $filename: $!\n";
        } 
    } 
}

print "$source copied in " . (time - $start) . " seconds.\n";    
}
Mickey
+5  A: 

IO::Dir is nice and provides a tied hash interface as well.

From the perldoc:

use IO::Dir;
$d = IO::Dir->new(".");
if (defined $d) {
    while (defined($_ = $d->read)) { something($_); }
    $d->rewind;
    while (defined($_ = $d->read)) { something_else($_); }
    undef $d;
}

tie %dir, 'IO::Dir', ".";
foreach (keys %dir) {
    print $_, " " , $dir{$_}->size,"\n";
}

So you could do something like:

tie %dir, 'IO::Dir', $directory_name; my @dirs = keys %dir;

Gary Richardson
+3  A: 

A quick and dirty solution is to use glob

@files = glob ('/path/to/dir/*');
Pat
+1  A: 

Similar to the above, but I think the best version is (slightly modified) from "perldoc -f readdir":

opendir(DIR, $somedir) || die "can't opendir $somedir: $!";
@dots = grep { (!/^\./) && -f "$somedir/$_" } readdir(DIR);
closedir DIR;
trjh
+3  A: 

You could use DirHandle:

    use DirHandle;
    $d = new DirHandle ".";
    if (defined $d) {
        while (defined($_ = $d->read)) { something($_); }
        $d->rewind;
        while (defined($_ = $d->read)) { something_else($_); }
        undef $d;
    }

DirHandle provides an alternative, cleaner interface to the opendir(), closedir(), readdir(), and rewinddir() functions.

David Precious
A: 

Unfortunately IO::Dir lacks documentation, so it's a royal pain in the backside to figure out how to get working. What the heck does read() return anyway? It doesn't appear to be a string.

Bah, I'm going to have to write my own if I can figure out how to make this one work, but I'd rather get IO::Dir working since it's already made. Hopefully someone will fix the documentation in the near future.

Randolf Richardson