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:

  • Barewordopendir DIR, $path creates a package-global dirhandle named DIR in the current package. It stays open until you closedir it explicitly or the program exits. Avoid in new code; the global namespace is shared across the whole package.

  • Undefined scalaropendir my $dh, $path autovivifies $dh into a reference to a fresh anonymous dirhandle. When $dh goes 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 $@. opendir reports through $! like the rest of the I/O family; $@ is untouched.

  • Reusing an open dirhandle. opendir on a handle that is already open silently closes the old directory before opening the new one. Prefer explicit closedir when the code is meant to be readable.

  • Relative paths. EXPR is resolved against the current working directory at the moment of the call. If you chdir later, 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. opendir follows symlinks by default: opening a link to a directory opens the target. The entries returned by readdir are the target’s entries, not the link.

  • Bareword vs lexical lifetime. A bareword dirhandle is global and outlives the enclosing block; a lexical my $dh is cleaned up when the scope ends. Mixing the two by accident (opendir DH, $path inside a sub) leaks dirhandles on repeated calls.

  • File vs dir handle mix-up. You cannot print to a dirhandle nor readdir from a filehandle. The error surfaces at the later call, not at opendir.

  • glob vs opendir. glob expands shell-style patterns and returns paths; opendir enumerates raw directory entries (including . and ..) without interpretation. Use glob when you want pattern matching; use opendir when 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 with opendir

  • closedir — release a dirhandle; implicit when a lexical $dh goes out of scope

  • rewinddir — reset the read position to the start without reopening

  • seekdir — reposition the dirhandle to a token previously returned by telldir

  • telldir — current read position as an opaque token suitable for seekdir

  • glob — pattern-based alternative when you want shell-style filename expansion instead of raw directory enumeration