scandir(3)
โจ NAME โจ
scandir, scandirat, alphasort, versionsort โ scan a directory for matching entries ๐๏ธโจ
So quick lil intro bestie: this whole page is about the Library Functions Manual side of scandir(3) โ three is the library-functions section, and yes we ARE in our library era ๐
๐
๐ LIBRARY ๐
Standard C library (libc, -lc) โ the OGs, the blueprint, no cap ๐ซถ
๐ซถ SYNOPSIS ๐ซถ
okay so here's the shape of it, and I need you to read it exactly, because compilers do not have a sense of humour:
#include <dirent.h>
int scandir(const char *restrict dirp,
struct dirent ***restrict namelist,
typeof(int (const struct dirent *)) *filter,
typeof(int (const struct dirent **, const struct dirent **))
*compar);
int alphasort(const struct dirent **a, const struct dirent **b);
int versionsort(const struct dirent **a, const struct dirent **b);
#include <fcntl.h> /* Definition of AT_* constants */
#include <dirent.h>
int scandirat(int dirfd, const char *restrict dirp,
struct dirent ***restrict namelist,
typeof(int (const struct dirent *)) *filter,
typeof(int (const struct dirent **, const struct dirent **))
*compar);
๐ Feature Test Macro Requirements for glibc (see feature_test_macros(7)):
Ah yes, the part that gatekeeps your code from compiling ๐ญ Here's who needs what:
scandir(), alphasort():
/* Since glibc 2.10: */ _POSIX_C_SOURCE >= 200809L
|| /* glibc <= 2.19: */ _BSD_SOURCE || _SVID_SOURCE
versionsort():
_GNU_SOURCE
scandirat():
_GNU_SOURCE
Basically: _GNU_SOURCE is the golden ticket for the glibc-exclusive girlies, versionsort() and scandirat(). And scandir()/alphasort() want _POSIX_C_SOURCE >= 200809L on modern glibc, or the vintage _BSD_SOURCE / _SVID_SOURCE if you're on glibc 2.19 or older. Vintage-core fr ๐
๐ DESCRIPTION ๐
Okay so the main character: scandir() scans the directory dirp, and calls filter() on every single directory entry it sees. Entries where filter() returns nonzero get stored in strings allocated via malloc(3), then sorted using qsort(3) with the comparison function compar(), and finally collected in the array namelist which is allocated via malloc(3). Three mallocs of commitment, slay ๐ซถ
And if filter is NULL? All entries are selected. Every last one. No filtering, we're accepting everyone into the group chat ๐ฑโจ
alphasort() and versionsort() are your go-to compar() girls:
alphasort()โ sorts directory entries usingstrcoll(3)versionsort()โ sorts usingstrverscmp(3)
Both of them do their thing on the strings (*a)->d_name and (*b)->d_name โ that's literally the field they're comparing, no cap.
๐ scandirat()
scandirat() operates in exactly the same way as scandir() โ same energy, same everything โ with the following differences described here. And the differences are all about how a relative path gets resolved ๐
- If
dirpis relative, it gets interpreted relative to the directory referred to by the file descriptordirfdโ rather than relative to the current working directory of the calling process, which is whatscandir()does for a relative pathname. Soscandirat()is the one that lets you be like "no bestie, look over here" ๐ซต - If
dirpis relative anddirfdis the special valueAT_FDCWD, thendirpis interpreted relative to the current working directory of the calling process โ exactly likescandir()does it. SoAT_FDCWDis the "never mind, do the normal thing" button ๐ - If
dirpis absolute, thendirfdis ignored. Periodt. Absolute paths don't need a plus-one ๐
Curious why anyone would even need scandirat()? See openat(2) for an explanation of the need for it. It's giving "there's lore here" ๐ง
๐ RETURN VALUE ๐
scandir() returns the number of directory entries selected. That's it, that's the number, count it ๐
On error it returns -1, with errno set to indicate the error. Classic. ๐
alphasort() and versionsort() return an integer that is less than, equal to, or greater than zero if the first argument is considered to be respectively less than, equal to, or greater than the second. Standard comparison-function choreography, they ate ๐
๐จ ERRORS ๐จ
Here's the list of ways it can go wrong, and I want you to know them all so you're never caught off guard:
EBADFโ (scandirat())dirpis relative butdirfdis neitherAT_FDCWDnor a valid file descriptor. So you handed it a fd that isn't a real one and it's not the magicAT_FDCWDeither. Rude awakening ๐ญENOENTโ The path indirpdoes not exist. It's not there. It never was. Ghosted ๐ซฅENOMEMโ Insufficient memory to complete the operation. Your RAM said "not today" ๐ENOTDIRโ The path indirpis not a directory. You pointed at something that isn't a folder, bestie, that's a whole different vibe ๐ธENOTDIRโ (scandirat())dirpis relative anddirfdis a file descriptor referring to a file other than a directory. Yes,ENOTDIRshows up twice on this list. That's not a typo, that's the original, and we respect the double appearance โจ (iconic behaviour honestly)
๐ ATTRIBUTES ๐
For an explanation of the terms used in this section, see attributes(7). Here's the tea, laid out in bullet form because we don't do tables in this house:
scandir(),scandirat()โ Thread safety:MT-Safe๐งตโalphasort(),versionsort()โ Thread safety:MT-Safe locale๐งต๐
So the sort girls are locale-sensitive when it comes to threading, keep that in mind ๐
๐ STANDARDS ๐
Which standards claimed which function, alphabetically by function:
alphasort()scandir()โ POSIX.1-2008.versionsort()scandirat()โ GNU.
So two of them are POSIX-certified and two of them are GNU exclusives. Know your lineages ๐
๐ฐ๏ธ HISTORY ๐ฐ๏ธ
The origin stories, bestie:
alphasort()scandir()โ 4.3BSD, POSIX.1-2008.versionsort()โ glibc 2.1.scandirat()โ glibc 2.15.
Ancient code energy. They've been doing this since before you were born fr fr ๐ธ
๐ NOTES ๐
Important lore drops, don't skip these:
- Since glibc 2.1,
alphasort()callsstrcoll(3). Earlier, it usedstrcmp(3). So the behaviour changed at 2.1 โ glow-up era ๐ - Before glibc 2.10, the two arguments of
alphasort()andversionsort()were typed asconst void *. Then whenalphasort()was standardized in POSIX.1-2008, the argument type was specified as the type-safeconst struct dirent **, and glibc 2.10 changed the definition ofalphasort()(and the nonstandardversionsort()) to match the standard. Type safety said "I'm the main character now" โจ
๐ EXAMPLES ๐
The program below prints a list of the files in the current directory in reverse order. It's short, it's cute, it's a whole vibe ๐ฅ
๐ง Program source
#define _DEFAULT_SOURCE
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
int
main(void)
{
struct dirent **namelist;
int n;
n = scandir(".", &namelist, NULL, alphasort);
if (n == -1) {
perror("scandir");
exit(EXIT_FAILURE);
}
while (n--) {
printf("%s\n", namelist[n]->d_name);
free(namelist[n]);
}
free(namelist);
exit(EXIT_SUCCESS);
}
Notice how it walks n downwards from the end โ that's where the reverse order comes from, and it also frees every namelist[n] before freeing namelist itself. Cleanup queen behaviour ๐ซถโจ
๐ SEE ALSO ๐
Go read these next, they're all in the same friend group:
closedir(3), fnmatch(3), opendir(3), readdir(3), rewinddir(3), seekdir(3), strcmp(3), strcoll(3), strverscmp(3), telldir(3)
and that's the whole page bestie!! ๐ this was Linux man-pages 6.20, dated 2026-02-08, and it's literally the scandir(3) page. go forth and scan some directories, you absolute icon ๐๐ธ stay safe, free your mallocs, and never forget that ENOTDIR is listed twice ๐โจ
๐ฆ where this page comes from
- arch rolling: man-pages 6.20-1 ยท BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
- nixpkgs unstable: man-pages 6.20
- ๐ the page's license: Linux-man-pages-copyleft
- Copyright 1993, David Metcalfe <david@prism.demon.co.uk>
- Copyright 2012, Mark R. Bannister <mark@proseconsulting.co.uk>
- Copyright 2006, Michael Kerrisk <mtk.manpages@gmail.com>
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms