I/O

seekdir#

Restore a directory handle to a position previously captured by telldir.

seekdir moves the read cursor of DIRHANDLE to POS, so that the next readdir call returns the entry that would have been returned when POS was first obtained from telldir on the same handle. It is the directory counterpart of seek on a file handle — with the crucial restriction that POS is not a byte offset you can compute, it is an opaque cookie you must have taken from telldir earlier.

Synopsis#

seekdir DIRHANDLE, POS

What you get back#

No meaningful value. seekdir returns what the underlying seekdir(3) returns, which on Linux is nothing useful — do not branch on the return value. To detect trouble, check $! after the call, or verify that the handle is still open before calling.

Typical use#

You are walking a directory, notice an entry you want to revisit later, and remember where you were so you can return to it after reading on:

opendir my $dh, $path or die "opendir $path: $!";

my %bookmarks;
while (my $entry = readdir $dh) {
    $bookmarks{$entry} = telldir $dh  # position AFTER reading $entry
        if $entry =~ /\.conf\z/;
}

# later: re-read the entry that follows the first .conf file
seekdir $dh, $bookmarks{(sort keys %bookmarks)[0]};
my $next = readdir $dh;

closedir $dh;

Returning to the start is the common special case — a seekdir $dh, 0 call is equivalent to rewinddir on conforming systems, but rewinddir is the idiomatic spelling for that intent:

seekdir $dh, 0;     # works, but say what you mean:
rewinddir $dh;      # clearer

Edge cases#

  • POS must come from telldir on the same handle. Passing an integer you made up, a value from a different handle, or a stale value from a handle that has since been closed and re-opened is undefined behaviour. The kernel may accept it silently and hand you garbage on the next readdir, skip entries, or return duplicates.

  • Directory compaction between telldir and seekdir: if the filesystem has compacted or reorganised the directory between the two calls (files created, deleted, or renamed in the same directory by any process), the POS cookie may no longer refer to the same entry — or to any entry at all. This is a property of the underlying seekdir(3) and is not a pperl limitation. For stable iteration over a changing directory, snapshot the list with readdir in list context and work off the copy.

  • Unopened or closed handle: sets $!. Under use warnings you get a seekdir() attempted on invalid dirhandle warning. Nothing in the return value tells you this happened — check $! or use defined fileno($dh) / explicit bookkeeping if you need to guard.

  • Not a byte offset: POS is an opaque token. Arithmetic on it (seekdir $dh, $pos + 1) is meaningless; the only safe values are 0 (start of directory, and even then prefer rewinddir) and values returned by telldir.

  • Entry order is unspecified: seekdir replays whatever order the kernel originally returned. That order is not sorted, not alphabetical, and not guaranteed to match a fresh opendir of the same path later. If your code sorts, sort explicitly each time.

  • SEEK_CUR-style relative seeks do not exist for directory handles. There is no equivalent of seek’s whence argument.

Differences from upstream#

Fully compatible with upstream Perl 5.42.

See also#

  • telldir — produce the POS cookie that seekdir consumes; the two are only useful as a pair

  • readdir — read the next entry; resumes from the position seekdir just set

  • rewinddir — the idiomatic way to return to the start of a directory; prefer it over seekdir $dh, 0

  • opendir — open the directory and obtain the handle seekdir operates on

  • seek — the file-handle analogue, but with a true byte offset and a whence argument; directory handles have neither