在Linux上使用C语言进行递归文件删除

12
我有一个C程序,在程序的某个时刻有这样一个代码块:
system("rm -rf foo");

假设 foo 是一个目录。我决定不使用调用系统的方式,而是在代码中实现递归删除,因为这样会更好。我认为能够找到一个完成此操作的代码片段很容易。可惜我错了。最终,我写了下面的代码:

#include <stdio.h>
#include <sys/stat.h>
#include <dirent.h>
#include <libgen.h>

int recursiveDelete(char* dirname) {

  DIR *dp;
  struct dirent *ep;

  char abs_filename[FILENAME_MAX];

  dp = opendir (dirname);
  if (dp != NULL)
    {
      while (ep = readdir (dp)) {
        struct stat stFileInfo;

        snprintf(abs_filename, FILENAME_MAX, "%s/%s", dirname, ep->d_name);

        if (lstat(abs_filename, &stFileInfo) < 0)
          perror ( abs_filename );

        if(S_ISDIR(stFileInfo.st_mode)) {
          if(strcmp(ep->d_name, ".") && 
             strcmp(ep->d_name, "..")) {
            printf("%s directory\n",abs_filename);
            recursiveDelete(abs_filename);
          }
        } else {
          printf("%s file\n",abs_filename);
                  remove(abs_filename);
        }
          }
      (void) closedir (dp);
        }
  else
    perror ("Couldn't open the directory");


  remove(dirname);
  return 0;

}

这似乎可以工作,但我太害怕在生产环境中实际使用它。我肯定做错了什么。在C语言中有没有递归删除的方法我错过了吗?或者有人能指出我犯的任何错误吗?


4
实际上,当你将某些东西传递给Shell时,它会传递给操作系统并以机器码形式运行。Unix内核非常优化,因此即使您完全按照Unix内核中的编码方式进行编码,您可能也只能看到5-10%的性能增加。传递给Shell太容易且方便了。 - stevendesu
1
有趣的是:POSIX规范中remove()函数对于非目录(文件、符号链接等)使用unlink(),对于目录则使用rmdir()。 你应该检查remove()函数是否正常工作,并在出现问题时进行报告。你应该看看如果函数的参数不是目录会发生什么 - 它会抱怨无法打开目录(但不说是哪一个;这也很糟糕),然后无论如何都会删除它(或尝试删除)。 - Jonathan Leffler
4个回答

13

POSIX有一个名为ftw(3)(文件树遍历)的函数,它可以

遍历位于目录dirpath下的目录树,并对树中每个条目调用fn()一次。


是的。POSIX来拯救了。它可能更安全、更快,并且可以捕获在手动遍历的正常操作中不总是能够捕获到的小错误。 - Matt Joiner
3
还有一个新的FTW函数 - nftw,它的界面略有不同。 - Jonathan Leffler
4
你需要的是nftw函数,参见同样问题的这个答案 - caf

2
我建议你可以采取一项额外的预防措施。
几乎总是在删除多个文件和/或目录时,在执行任何可能破坏此目录之外数据的操作之前,最好先使用chroot()进入该目录。

1

你因为害怕而获得了赞誉,这是在这种情况下应该具有的健康态度。

我没有推荐的库可以提供,您有两个选择:

1)详尽地运行此代码
  a)不在计算机上;在纸上用铅笔。获取现有的目录树,列出所有元素并通过每个步骤运行程序,验证其是否正常工作。
  b)编译代码,但将所有删除调用替换为执行printf语句的行-验证它是否执行了应该执行的操作。
  c)重新插入删除调用并运行。

2)使用您的原始方法(调用system()函数)。


1
我认为在调用recursiveDelete()之前需要调用closedir()(因为您不希望/不需要所有目录都打开,当您进入它们时)。还要在调用remove()之前调用closedir(),因为remove()可能会在打开的目录上出现错误。您应该仔细地遍历一次,以确保readdir()不会捕获“..”。此外,要注意链接目录,您可能不想递归到符号或硬链接的目录中。

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