【问题标题】:Check if a directory is empty using C on Linux在 Linux 上使用 C 检查目录是否为空
【发布时间】:2011-09-17 00:51:26
【问题描述】:

这是在 C 中检查目录是否为空的正确方法吗?是否有更有效的方法来检查空目录,特别是如果它有 1000 个文件(如果不是空的)?

int isDirectoryEmpty(char *dirname) {
  int n = 0;
  struct dirent *d;
  DIR *dir = opendir(dirname);
  if (dir == NULL) //Not a directory or doesn't exist
    return 1;
  while ((d = readdir(dir)) != NULL) {
    if(++n > 2)
      break;
  }
  closedir(dir);
  if (n <= 2) //Directory Empty
    return 1;
  else
    return 0;
}

如果它是一个空目录,readdir 将在条目 '.' 之后停止。和 '..' 如果n&lt;=2 则为空。

如果为空或不存在,则返回1,否则返回0

更新:

@c$ time ./isDirEmpty /fs/dir_with_1_file; time ./isDirEmpty /fs/dir_with_lots_of_files
0

real    0m0.007s
user    0m0.000s
sys 0m0.004s

0

real    0m0.016s
user    0m0.000s
sys 0m0.008s

为什么与只有一个文件的目录相比,检查包含大量文件的目录需要更长的时间?

【问题讨论】:

    标签: c linux directory


    【解决方案1】:

    有没有更有效的检查方法 对于一个空目录,尤其是如果 如果不为空,它有 1000 个文件

    你编写代码的方式与它有多少文件无关(你break 如果 n > 2)。因此,您的代码最多使用 5 次调用。我认为没有任何方法可以(便携地)让它更快。

    【讨论】:

    • 请阅读我的编辑,为什么相同的代码在有很多文件的目录上运行比只有一个文件的目录需要更长的时间?
    • readdir(3) 是 getdents(2) 的前端函数。 strace 中对 getdents() 的系统调用显示它尝试从目录中检索 32768 个条目,并检索到 1175 个条目。我想如果您继续使用 readdir(3),您将无法解决这个问题。尽管 getdents() 手册页说您最好不要查看该函数,但如果您不关心可移植性,您可能希望使用该调用。
    • @Friek 没错。 getdents(2) 出现在许多其他实施中,但不是标准的。
    【解决方案2】:
    bool has_child(string path)
    {
        if(!boost::filesystem::is_directory(path))
            return false;
    
        boost::filesystem::directory_iterator end_it;
        boost::filesystem::directory_iterator it(path);
        if(it == end_it)
            return false;
        else
            return true;
    }
    

    【讨论】:

    • @freethinker 使用 C 进行编码。C 中不存在布尔值,除非您使用以下内容定义它们:typedef enum { false, true } bool;
    • @Larrimus ...或使用ISO-C99,它有一个标准定义的关键字_Bool,并有一个标准定义的包装器stdbool.h,它提供标准定义的类型bool。只是布尔值的仅供参考,而不是其余部分。抱歉加入得太晚了。
    【解决方案3】:

    https://en.cppreference.com/w/cpp/filesystem/is_empty

    在最新的 c++ 中,我们可以使用上面链接的新 api "std::filesystem::is_empty" 检查 dir 是否为空。

    【讨论】:

      【解决方案4】:

      可能有一个棘手的策略,称为命令行rmdir,它无法删除非空目录,并且此功能可用于检测目录是否为空。为此,请尝试通过调用system("rmdir your_directory") 来删除目录。如果目录不为空,则该函数将失败并返回一个非零值,并且可能会提示您rmdir: failed to remove 'your_directory': Directory not empty。可以通过将stderr 重定向到/dev/null 来使提示静音,并且静音可以提高其性能。否则,该目录将被删除,然后您可以通过重新创建它来恢复它。

      如果目录中有任何隐藏文件,此策略将很有帮助,它仍然能够检测到它们的存在。并且rmdir立即返回,无论在我的情况下非空目录中有多少文件。

      但是要注意命令别名,尤其是在*nix shell 环境中,如果有rmdir 命令的别名添加了一些导致rmdir 进行递归文件删除的参数,该技巧将失败并导致所有要实际上删除的目录。这可以通过调用删除别名的system("\rmdir your_directory") 来解决。

      【讨论】:

        【解决方案5】:

        也许这段代码可以帮助你:

        #include <stdio.h>
        #include <stdlib.h>
        #include <string.h>
        
        int main(int argc, char *argv[]) {
            char *cmd;
            char *folder = "/tmp";
            int status, exitcode;
            char *format="test $(ls -AU \"%s\" 2>/dev/null | head -1 | wc -l) -ne 0";
            clock_t start, stop;
            int size;
        
            if(argc == 2)
                    folder = argv[1];
        
            size = strlen(format)+strlen(folder)+1;
            cmd = malloc(size * sizeof(char));
        
            snprintf(cmd, size, format, folder);
            printf("executing: %s\n", cmd);
        
            status = system(cmd);
            exitcode = WEXITSTATUS(status);
        
            printf ("exit code: %d, exit status: %d\n", exitcode, status);
        
            if (exitcode == 1)
                    printf("the folder is empty\n");
            else
                    printf("the folder is non empty\n");
        
            free(cmd);
            return 0;
        }
        

        我使用 ls -AU folder 2 检查文件夹是否为空>/dev/null |头-1 | wc -l,计算文件夹中的文件,如果返回零则文件夹为空,否则文件夹非空。 WEXITSTATUS 宏,返回执行命令的退出代码。 head 命令不会等到 ls 完成,而是等到条件满足。

        使用 find 命令生成长文件列表的一些例子表明它确实有效

        无头命令

        /usr/bin/time -p -v find / -print | wc -l
        
        output
        Command being timed: "find / -print"
            User time (seconds): 0.63
            System time (seconds): 1.28
            Percent of CPU this job got: 98%
            Elapsed (wall clock) time (h:mm:ss or m:ss): 0:01.94
            Average shared text size (kbytes): 0
            Average unshared data size (kbytes): 0
            Average stack size (kbytes): 0
            Average total size (kbytes): 0
            Maximum resident set size (kbytes): 6380
            Average resident set size (kbytes): 0
            Major (requiring I/O) page faults: 0
            Minor (reclaiming a frame) page faults: 3419
            Voluntary context switches: 7
            Involuntary context switches: 140
            Swaps: 0
            File system inputs: 0
            File system outputs: 0
            Socket messages sent: 0
            Socket messages received: 0
            Signals delivered: 0
            Page size (bytes): 4096
            Files counted: 1043497
        

        使用 head 修改的命令

        /usr/bin/time -p -v find / -print | head -1 | wc -l
        
        Command terminated by signal 13
            Command being timed: "find / -print"
            User time (seconds): 0.00
            System time (seconds): 0.00
            Percent of CPU this job got: 100%
            Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.00
            Average shared text size (kbytes): 0
            Average unshared data size (kbytes): 0
            Average stack size (kbytes): 0
            Average total size (kbytes): 0
            Maximum resident set size (kbytes): 2864
            Average resident set size (kbytes): 0
            Major (requiring I/O) page faults: 0
            Minor (reclaiming a frame) page faults: 136
            Voluntary context switches: 1
            Involuntary context switches: 0
            Swaps: 0
            File system inputs: 0
            File system outputs: 0
            Socket messages sent: 0
            Socket messages received: 0
            Signals delivered: 0
            Page size (bytes): 4096
            Files counted: 1
        

        如您所见,第一个不带“head”的命令执行需要 1.28 秒,而使用“head”修改的命令执行需要 0 秒。

        此外,如果我们测量上述核心的执行时间,我们有没有头。

        普通ls:

        /usr/bin/time -p ls -A /var/lib/dpkg/info/
        real 0.67
        user 0.06
        sys 0.06
        

        无头程序

        /usr/bin/time -p ./empty.exe /var/lib/dpkg/info/
        executing: test $(ls -AU "/var/lib/dpkg/info/" 2>/dev/null | wc -l) -ne 0
        exit code: 0, exit status: 0
        the folder is non empty
        real 0.01
        user 0.00
        sys 0.01
        

        使用 head 编程

        /usr/bin/time -p ./empty.exe /var/lib/dpkg/info/
        executing: test $(ls -AU "/var/lib/dpkg/info/" 2>/dev/null | head -1 | wc -l) -ne 0
        exit code: 0, exit status: 0
        the folder is non empty
        real 0.00
        user 0.00
        sys 0.00
        

        注意:如果文件夹不存在,或者你没有正确的权限访问它,这个程序必须打印“文件夹为空”。

        该程序是使用:gcc empty.c -o empty.exe 构建的

        【讨论】:

        • -1 OP 要求速度,这显然不快,此外它不会转义给定的字符串并且使用的缓冲区太短,因此它很容易受到 shell 注入并且可能意外截断给定的路径,可能会导致其他错误。 – 简而言之:这既是一个糟糕的例子一个非常糟糕的例子。
        • 首先,不到 100 毫秒对于弄清楚目录是否为空这样简单的事情来说是一个很长的时间。想象一下使用它的 Web 服务器的性能下降……如果这甚至达到基于 readdir 的解决方案的数量级,我会感到非常惊讶。我同意盲目地复制粘贴通常不是一个好主意,但是没有经验的人会这样做,因此代码中的问题会成倍增加。此外,我不会不加思索就对任何答案给出负面评价——顺便说一句,你的答案仍然有外壳注入,至少截断是固定的。
        • 我认为,如果我们通过对代码提出建议来改进它而不是批评我们做出贡献,那么社区将会发展得最快。我做出了贡献,你呢?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-11-21
        • 2018-11-20
        • 1970-01-01
        • 2018-12-23
        • 2012-07-04
        • 2017-07-25
        • 1970-01-01
        相关资源
        最近更新 更多