SCANDIR(3)
โจ SCANDIR(3) โจ
okay so bestie, we're in section 3 of the Library Functions Manual today, which means we're hanging out in the Standard C Library (libc, -lc) and we're gonna talk about scanning directories. yes. that thing. it's giving "the most useful thing you'll ever do with a directory" and it lowkey slays ๐
๐ NAME ๐
scandir, fdscandir, scandirat, scandir_b, fdscandir_b, fdscandirat_b, alphasort, versionsort โ scan a directory ๐
(and yes, fdscandirat_b really is in the NAME list even though the rest of the page only ever talks about scandirat_b, iykyk โ I'm not the one who wrote the header, I'm just the one telling you about it ๐ซถ)
๐ธ LIBRARY ๐ธ
Standard C Library (libc, -lc)
๐ฆ SYNOPSIS ๐ฆ
okay bestie, get your #include ready:
#include <dirent.h>
then, the icons themselves:
int
scandir(const char *dirname, struct dirent ***namelist,
int (*select)(const struct dirent *),
int (*compar)(const struct dirent **, const struct dirent **));
int
fdscandir(int dirfd, struct dirent ***namelist,
int (*select)(const struct dirent *),
int (*compar)(const struct dirent **, const struct dirent **));
int
scandirat(int dirfd, const char *dirname, struct dirent ***namelist,
int (*select)(const struct dirent *),
int (*compar)(const struct dirent **, const struct dirent **));
int
scandir_b(const char *dirname, struct dirent ***namelist,
int (^select)(const struct dirent *),
int (^compar)(const struct dirent **, const struct dirent **));
int
fdscandir_b(int dirfd, struct dirent ***namelist,
int (^select)(const struct dirent *),
int (^compar)(const struct dirent **, const struct dirent **));
int
scandirat_b(int dirfd, const char *dirname, struct dirent ***namelist,
int (^select)(const struct dirent *),
int (^compar)(const struct dirent **, const struct dirent **));
int
alphasort(const struct dirent **d1, const struct dirent **d2);
int
versionsort(const struct dirent **d1, const struct dirent **d2);
notice the ^ blocks on the _b ones? that's the whole vibe. block energy ๐ฅ
๐ DESCRIPTION ๐
the main character: scandir() ๐
So scandir() reads the directory dirname and builds up an array of pointers to directory entries, using malloc(3). it hands you back the number of entries in the array. and the pointer to that array of directory entries? it gets stored in the location referenced by namelist โ even if no entries were selected. yes, even then. it's still there for you, bestie ๐ซถ
select โ the vibe check ๐ง
The select argument is a pointer to a user-supplied subroutine that scandir() calls to decide which entries make the cut for the array. select gets handed a pointer to a directory entry, and it should return a non-zero value if that entry should be included. if select is null? all the entries get in. everyone's invited, no bouncer ๐
compar โ the sorting agenda ๐
The compar argument is a pointer to a user-supplied subroutine that gets passed to qsort(3) to sort the finished array. if this pointer is null, the array is just not sorted. chaos. we love her ๐ธ
the two ready-made comparators โญ
alphasort()โ a routine you can totally use as thecomparargument. it sorts the array alphabetically usingstrcoll(3).versionsort()โ a routine you can also use as thecomparargument. it sorts the array naturally usingstrverscmp(3). sofile2actually comes beforefile10, the way nature intended ๐ญ
cleanup time ๐งน
the memory allocated for the array can be deallocated with free(3) โ you free each pointer in the array first, and then the array itself. order matters, bestie. don't skip leg day, don't skip the inner frees.
the fd variants ๐ฆ
fdscandir() is basically scandir() but it takes a file descriptor that references a directory instead of a path. one thing though, no cap: the file descriptor is left open on return, regardless of outcome. even if things went sideways, it's still open. so that's on you to handle ๐
scandirat() is scandir()'s sibling with an extra dirfd argument. here's the breakdown:
- if the
dirnameyou supplied is absolute, it behaves identically toscandir()and thedirfdargument is just... unused. sitting there. vibing. ๐ - if
dirnameis relative, thendirfdhas to be a valid file descriptor referencing a directory, and thedirnamelookup happens relative to the directory thatdirfdpoints at. - if
dirfdhas the special valueAT_FDCWD, then the current process directory is used as the base for relative lookups. - and for all the extra details, go see
openat(2). ๐
the _b girlies ๐
scandir_b(), fdscandir_b(), and scandirat_b() behave exactly the same way as scandir(), fdscandir(), and scandirat() respectively โ except they take blocks as arguments instead of function pointers, and they call qsort_b() rather than qsort(). that's the entire difference. slay. ๐ฅ
๐ DIAGNOSTICS ๐
so scandir(), fdscandir(), scandirat(), scandir_b(), fdscandir_b(), and scandirat_b() all return the number of directory entries found on success. ๐
but if:
- the directory can't be opened for reading, or
- an error happens while reading the directory, or
malloc(3)can't allocate enough memory to hold all the directory entries,
then they return -1 and set errno to an appropriate value. ๐ญ it's giving "it's not me, it's errno".
๐ ERRORS ๐
scandir(),scandirat(),scandir_b(), andscandirat_b()may fail and seterrnofor any of the errors specified for theopendir(3),malloc(3),readdir(3), andclosedir(3)functions.fdscandir()andfdscandir_b()may fail and seterrnofor any of the errors specified for thefdopendir(3),malloc(3),readdir(3), andclosedir(3)functions.
(spot the difference: opendir(3) vs fdopendir(3). that's it. that's the whole switch-up ๐ซถ)
๐ SEE ALSO ๐
openat(2), directory(3), malloc(3), qsort(3), strcoll(3), strverscmp(3), dir(5)
go read them. or don't. but like, they're right there ๐
๐ STANDARDS ๐
alphasort()andscandir()are expected to conform to IEEE Std 1003.1-2008 (โPOSIX.1โ). iconic, certified, periodt โscandirat()andversionsort()are GNU extensions and conform to no standard. they're doing their own thing, no rules, main character energy ๐fdscandir(),scandir_b(),fdscandir_b(), andscandirat_b()are FreeBSD extensions. ๐ฆ
๐ง HISTORY ๐ง
scandir()andalphasort()appeared in 4.2BSD. the elders. the originals. ๐scandir_b()was added in FreeBSD 11.0.scandirat()andversionsort()were added in FreeBSD 13.2.fdscandir(),fdscandir_b(), andscandirat_b()were added in FreeBSD 15.0. the babies of the family ๐
okay that's the whole page bestie, go forth and scandir() responsibly ๐โจ๐
this edition came to you from Debian, dated June 25, 2025, and that's the SCANDIR(3) sign-off โ mwah ๐
๐ฆ where this page comes from
- freebsd 15.1: base 15.1
- Copyright (c) 1983, 1991, 1993 The Regents of the University of California. All rights reserved
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms