The opendir
, readdir
, and closedir
functions operate on directories as open
, < >, and close
operate on files. Both use handles, but the directory handles used by opendir
and friends are different from the file handles used by open
and friends. In particular, you can't use < > on a directory handle.
In scalar context, readdir
returns the next filename in the directory until it reaches the end of the directory when it returns undef
. In list context it returns the rest of the filenames in the directory or an empty list if there were no files left. As explained in the Introduction, the filenames returned by readdir
do not include the directory name. When you work with the filenames returned by readdir
, you must either move to the right directory first or prepend the directory to the filename.
This shows one way of prepending:
$dir = "/usr/local/bin";
print "Text files in $dir are:\n";
opendir(BIN, $dir) or die "Can't open $dir: $!";
while( defined ($file = readdir BIN) ) {
print "$file\n" if -T "$dir/$file";
}
closedir(BIN);
We test $file
with defined
because simply saying while
($file
=
readdir
BIN)
would only be testing truth and not definedness. Although the loop would end when readdir
ran out of files to return, it would also end prematurely if a file had the name "0"
.
The readdir
function will return the special directories "."
(the directory itself) and ".."
(the parent of the directory). Most people skip the files with code like:
while ( defined ($file = readdir BIN) ) {
next if $file =~ /^\.\.?$/; # skip . and ..
# ...
}
Like filehandles, directory handles are per-package constructs. Further, you have two ways of getting a local directory handle: use local
*DIRHANDLE
or use an object module (see Recipe 7.16). The appropriate module in this case is DirHandle
. The following code uses DirHandle and produces a sorted list of plain files that aren't dotfiles (that is, whose names don't begin with a "."
):
use DirHandle;
sub plainfiles {
my $dir = shift;
my $dh = DirHandle->new($dir) or die "can't opendir $dir: $!";
return sort # sort pathnames
grep { -f } # choose only "plain" files
map { "$dir/$_" } # create full paths
grep { !/^\./ } # filter out dot files
$dh->read()
; # read all entries
}
DirHandle's read
method behaves just like readdir
, returning the rest of the filenames. The bottom grep
only returns those that don't begin with a period. The map
turns the filenames returned by read
into fully qualified filenames, and the top grep
filters out directories, links, etc. The resulting list is then sort
ed and returned.
In addition to readdir
, there's also rewinddir
(to move the directory handle back to the start of the filename list), seekdir
(to move to a specific offset in the list), and telldir
(to find out how far from the start of the list you are).