如何在C语言中列出给定目录中的所有子目录?

6
有没有一种方法可以在C语言中列出给定目录路径中的所有子目录?我希望能够使用stat()函数来实现,但它只适用于文件。

Richie:stat() 应该至少能够告诉我们它是一个类 UNIX 的操作系统。 - Joey
1
你说得对,stat() 函数可以用于文件,但是目录条目也是文件。因此,stat("/etc/passwd", &buf)stat("/etc/", &buf) 都可以使用。 - Sean Bright
操作系统是Linux。 - KenjiOne
我的总体目标是使用线程来查找给定目录及其子目录的大小。我试图通过找到给定目录中有多少个子目录,然后创建相应数量的线程来实现这一目标。 - KenjiOne
这并不是一个真正的C语言问题,而是一个平台问题(我猜是Linux),只是在最后加上了“我正在使用C语言”的一句话。 - T.E.D.
4个回答

12

stat 命令也适用于目录。

#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>

int num_dirs(const char* path)
{
    int dir_count = 0;
    struct dirent* dent;
    DIR* srcdir = opendir(path);

    if (srcdir == NULL)
    {
        perror("opendir");
        return -1;
    }

    while((dent = readdir(srcdir)) != NULL)
    {
        struct stat st;

        if(strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
            continue;

        if (fstatat(dirfd(srcdir), dent->d_name, &st, 0) < 0)
        {
            perror(dent->d_name);
            continue;
        }

        if (S_ISDIR(st.st_mode)) dir_count++;
    }
    closedir(srcdir);
    return dir_count;
}


1
谢谢,我已经理解了你使用的很多函数,知道了发生了什么,并且它起作用了! - KenjiOne
1
因为这个答案看起来很好,我稍微编辑了一下,使其更加强大 - 用 fstatat 替换了 stat(这意味着你不必操心创建完整路径,并且避免了竞争条件),并处理了 opendirfstatat 的错误(在实践中很有可能出现,比如“权限被拒绝”之类的问题)。 - caf
@GuyL,很遗憾你的编辑被拒绝了。我不太了解直接UNIX API的东西,但是如果你添加正确的包含文件,这样很容易验证它是否可以编译通过,那么它可能更容易被批准。 - Rob Neuhaus

5

3
/*
I had need in something like this not so long ago (my difference is I
needed recursive scan) so I added only some comments... Sorry for recursion
but I was short of time and this was only part of internal one-time tool.
*/

/* Print all the dirs starting from <path> [maybe recursive]. */
int print_dirs(const char *path, int recursive)
{
    struct dirent *direntp = NULL;
    DIR *dirp = NULL;
    size_t path_len;

    /* Check input parameters. */
    if (!path)
        return -1;
    path_len = strlen(path);  

    if (!path || !path_len || (path_len > _POSIX_PATH_MAX))
        return -1;

    /* Open directory */
    dirp = opendir(path);
    if (dirp == NULL)
        return -1;

    while ((direntp = readdir(dirp)) != NULL)
    {
        /* For every directory entry... */
        struct stat fstat;
        char full_name[_POSIX_PATH_MAX + 1];

        /* Calculate full name, check we are in file length limts */
        if ((path_len + strlen(direntp->d_name) + 1) > _POSIX_PATH_MAX)
            continue;

        strcpy(full_name, path);
        if (full_name[path_len - 1] != '/')
            strcat(full_name, "/");
        strcat(full_name, direntp->d_name);

        /* Ignore special directories. */
        if ((strcmp(direntp->d_name, ".") == 0) ||
            (strcmp(direntp->d_name, "..") == 0))
            continue;

        /* Print only if it is really directory. */
        if (stat(full_name, &fstat) < 0)
            continue;
        if (S_ISDIR(fstat.st_mode))
        {
            printf("%s\n", full_name);
            if (recursive)
                print_dirs(full_name, 1);
        }
    }

    /* Finalize resources. */
    (void)closedir(dirp);
    return 0;
}

/* We are taking first argument as initial path name. */
int main(int argc, const char* argv[])
{
    if (argc < 2)
        return -1;

    print_dirs(argv[1], 1);
    return 0;
}

2

正如其他人所指出的,stat(2) 可以在所有类型的文件和设备上正常工作。它会读取到远端文件的符号链接;如果您需要有关符号链接本身的信息,请使用 lstat(2)

要列出单个目录中所有目录的名称(非递归),请使用 readdir(3) 函数系列的组合。

要递归列出所有目录的名称,请使用 ftw(3)nftw(3) 函数进行“文件树遍历”(从而得到它们的名称;'n' 代表 'new')。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接