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).