Filehandles, files, directories
opendir#
Open a directory for reading.
opendir attaches DIRHANDLE to the directory named by EXPR so the
directory’s entries can be read with readdir and the
position manipulated with telldir,
seekdir, and rewinddir. When you’re
done, closedir releases the handle. DIRHANDLE lives in
a namespace distinct from file handles — a bareword you have already
used with open is free to reuse here, and vice versa.
Synopsis#
opendir DIRHANDLE, EXPR
opendir my $dh, EXPR
What you get back#
1 on success, 0 on failure. On failure $! holds the
reason — the directory does not exist, is not a directory, is not
readable, or the process has hit its open-file limit. Always check
the return; a silent failure here turns into a mysteriously empty
readdir later.
opendir my $dh, $path
or die "opendir $path: $!";
Global state it touches#
$!— set to the system error string on failure, left untouched on success. Capture it before any other system call disturbs it.
Handle semantics#
Three forms of DIRHANDLE are accepted, and the choice matters:
Bareword —
opendir DIR, $pathcreates a package-global dirhandle namedDIRin the current package. It stays open until youclosedirit explicitly or the program exits. Avoid in new code; the global namespace is shared across the whole package.Undefined scalar —
opendir my $dh, $pathautovivifies$dhinto a reference to a fresh anonymous dirhandle. When$dhgoes out of scope and its refcount drops to zero, the handle is closed automatically. This is the form to use.Expression — any expression whose value is usable as an indirect dirhandle (typically a real dirhandle name held in a variable). The handle identity comes from the expression’s value, not its syntactic form.
Dirhandles and filehandles share the same underlying I/O object: a
given object can be open as one or the other at a time, never both.
Passing a dirhandle to read or a filehandle to
readdir is a usage error, not a silent read of zero
bytes.
Examples#
The canonical opendir / readdir / closedir
idiom:
opendir my $dh, $dir
or die "opendir $dir: $!";
while (my $entry = readdir $dh) {
next if $entry eq '.' or $entry eq '..';
print "$dir/$entry\n";
}
closedir $dh;
Read every entry into a list in one go. readdir in list
context returns all remaining entries:
opendir my $dh, '.' or die "opendir .: $!";
my @entries = grep { !/^\.\.?\z/ } readdir $dh;
closedir $dh;
Rely on lexical scope for cleanup — no explicit closedir
needed:
sub list_dir {
my ($path) = @_;
opendir my $dh, $path or return;
return grep { !/^\.\.?\z/ } readdir $dh;
# $dh goes out of scope here, handle is closed
}
Handle a missing-or-unreadable directory without dying:
if (opendir my $dh, $path) {
while (defined(my $e = readdir $dh)) { ... }
closedir $dh;
} else {
warn "skipping $path: $!";
}
Revisit the start of a directory with rewinddir rather
than reopening:
opendir my $dh, $dir or die $!;
my @first_pass = readdir $dh;
rewinddir $dh;
my @second_pass = readdir $dh;
closedir $dh;
Edge cases#
Failure sets
$!, not$@.opendirreports through$!like the rest of the I/O family;$@is untouched.Reusing an open dirhandle.
opendiron a handle that is already open silently closes the old directory before opening the new one. Prefer explicitclosedirwhen the code is meant to be readable.Relative paths.
EXPRis resolved against the current working directory at the moment of the call. If youchdirlater, a relative path you captured earlier may no longer mean the same thing — the already-opened dirhandle keeps pointing at the right inode regardless.Symlinks.
opendirfollows symlinks by default: opening a link to a directory opens the target. The entries returned byreaddirare the target’s entries, not the link.Bareword vs lexical lifetime. A bareword dirhandle is global and outlives the enclosing block; a lexical
my $dhis cleaned up when the scope ends. Mixing the two by accident (opendir DH, $pathinside a sub) leaks dirhandles on repeated calls.File vs dir handle mix-up. You cannot
printto a dirhandle norreaddirfrom a filehandle. The error surfaces at the later call, not atopendir.globvsopendir.globexpands shell-style patterns and returns paths;opendirenumerates raw directory entries (including.and..) without interpretation. Useglobwhen you want pattern matching; useopendirwhen you want every entry exactly as the filesystem stores it.
Differences from upstream#
Fully compatible with upstream Perl 5.42.
See also#
readdir— read the next entry (or all remaining entries) from a dirhandle opened withopendirclosedir— release a dirhandle; implicit when a lexical$dhgoes out of scoperewinddir— reset the read position to the start without reopeningseekdir— reposition the dirhandle to a token previously returned bytelldirtelldir— current read position as an opaque token suitable forseekdirglob— pattern-based alternative when you want shell-style filename expansion instead of raw directory enumeration