readdir#
Read the next entry, or all remaining entries, from a directory handle
opened by opendir.
readdir is the directory counterpart of readline: it
pulls names out of a directory handle one at a time in scalar context,
or all at once in list context. The handle must already have been
opened with opendir; iteration advances a position that
rewinddir, seekdir, and telldir
can manipulate.
Synopsis#
readdir DIRHANDLE
my $name = readdir $dh;
my @names = readdir $dh;
while (readdir $dh) { ... } # sets $_
What you get back#
Context-dependent:
Scalar context: the next entry’s name as a string, or
undefwhen the directory is exhausted.List context: every remaining entry as a list of strings, or the empty list when the directory is already exhausted.
Entries are bare filenames, not paths. readdir returns "foo.txt",
never "/some/dir/foo.txt". If you plan to hand the result to a file
test, open, stat, or any other call that resolves
paths against the current working directory, prepend the directory
yourself — readdir does not chdir into the directory
it is reading.
The list always includes the entries . (current directory) and ..
(parent directory) as reported by the underlying filesystem. Filter
them out explicitly if you do not want them:
my @real = grep { $_ ne '.' && $_ ne '..' } readdir $dh;
Order is filesystem-dependent, not alphabetical. ext4, XFS, tmpfs, NFS, and FAT each return entries in their own internal order, which may be insertion order, hash order, or something else entirely. Never assume sorted output. If you need a predictable order, sort the result:
my @names = sort readdir $dh;
Examples#
Read every name into a list, drop the dot entries, and sort:
opendir(my $dh, $some_dir) or die "opendir $some_dir: $!";
my @names = sort grep { !/^\.\.?\z/ } readdir $dh;
closedir $dh;
Iterate one entry at a time. A bare readdir in a while
condition assigns to $_ and tests the result for
definedness, not truth — so an entry literally named "0" still
keeps the loop running:
opendir(my $dh, $some_dir) or die "opendir $some_dir: $!";
while (readdir $dh) {
print "$some_dir/$_\n";
}
closedir $dh;
Filetest with the directory prepended — forgetting the prefix is the
classic readdir bug:
opendir(my $dh, $some_dir) or die "opendir $some_dir: $!";
my @dotfiles = grep { /^\./ && -f "$some_dir/$_" } readdir $dh;
closedir $dh;
Scalar context — one name per call, undef signals end of
directory:
opendir(my $dh, $some_dir) or die "opendir $some_dir: $!";
while (defined(my $name = readdir $dh)) {
next if $name eq '.' || $name eq '..';
process("$some_dir/$name");
}
closedir $dh;
Rewind and read a second time — the handle stays open and usable:
my @first = readdir $dh;
rewinddir $dh;
my @second = readdir $dh; # same entries, possibly different order
Edge cases#
Exhausted handle: further calls return
undefin scalar context, empty list in list context. Userewinddirto start over.Closed or invalid handle: returns
undef(scalar) or empty list (list) and sets$!. Underuse warningsPerl emitsreaddir() attempted on invalid dirhandle.Definedness test in
while: bothwhile (readdir $dh)andwhile (my $n = readdir $dh)(and theirforequivalents) are special-cased to testdefined, not truth. An entry named"0"is returned and the loop continues..and..are always present on POSIX filesystems, even in an otherwise empty directory. Filter them if you want only real content.No path prefix: entries are bare names. Any filetest or
openon the raw return value tests relative to the current working directory, not$some_dir.No sorting: the order is whatever the kernel returns. Apply
sortexplicitly if stability matters.Concurrent directory changes: entries added or removed during iteration may or may not appear, per the platform. The only safe assumption is that entries present at
opendirtime and not removed will be reported.Encoding:
readdirreturns byte strings as the kernel stores them. On a filesystem using UTF-8 filenames, applyEncode::decode_utf8or configure theopenpragma if you need character strings.
Differences from upstream#
Fully compatible with upstream Perl 5.42.
See also#
opendir— opens the directory handle thatreaddirreads from; always the first call in the sequenceclosedir— releases the handle once iteration is donerewinddir— resets the iteration position to the start of the directoryseekdir— jumps to a previously recorded positionglob— shell-style pattern matching over directory contents; higher-level when you want*.txtrather than every entry