在Linux中如何以编程方式获取目录大小?

7
我希望能够通过C程序获取Linux系统中特定目录的确切大小。 我尝试使用statfs(path,struct statfs &),但它不能给出确切的大小。 我还尝试使用stat(),但它返回任何目录的大小都为4096!
请建议一种方法,使我可以像“du -sh dirPath”命令一样获得目录的确切大小。
另外,我不想通过system()使用du。
提前致谢。

1
磁盘使用情况(du)和文件大小之和(stat)不是相同的概念。您需要哪一个? - Randy Proctor
在目录上运行stat命令并不会返回文件大小的总和。 它将返回目录本身使用的空间量。 - derobert
4个回答

11

基于Jim Plank的示例,让您轻松入手:

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

int main( int argc, char **argv ) {
  DIR *d = opendir( "." );

  if( d == NULL ) {
    fprintf( stderr, "Cannot open current working directory\n" );
    return 1;
  }

  struct dirent *de;
  struct stat buf;
  int total_size = 0;

  for( de = readdir( d ); de != NULL; de = readdir( d ) ) {
    int exists = stat( de->d_name, &buf );

    if( exists < 0 ) {
      fprintf( stderr, "Cannot read file statistics for %s\n", de->d_name );
    } else {
      total_size += buf.st_size;
    }
  }

  closedir( d );
  printf( "%d\n", total_size );

  return 0;
}

读者需要注意以下事项并思考以下问题:

  • 本示例不完整。请参阅Plank的笔记以获取更多详细信息。
  • 如果存在被锁定的文件会发生什么?
  • 是否需要特殊处理符号链接 (以避免无限循环)?
  • 如何为出错的文件输出完整路径名?

这个答案是一个起点,而不是一个完整和强大的计算目录大小的程序。如果您需要更多帮助,请查阅du程序的源代码。


5
你需要对当前目录和子目录中的所有文件进行stat()操作并将它们相加。
考虑使用递归算法来实现。

2
如果您不想使用'system',但可以使用'pipe''fork''execlp''du',那么您可以建立一个管道,派生一个新进程,在管道中重定向子进程的'STDOUT',在子进程中执行'du',并在父进程中读取结果。示例代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void) {
  int pfd[2], n;
  char str[1000];

  if (pipe(pfd) < 0) {
    printf("Oups, pipe failed.  Exiting\n");
    exit(-1);
  }

  n = fork();

  if (n < 0) {
    printf("Oups, fork failed.  Exiting\n");
    exit(-2);
  } else if (n == 0) {
    close(pfd[0]);

    dup2(pfd[1], 1);
    close(pfd[1]);

    execlp("du", "du", "-sh", "/tmp", (char *) 0);
    printf("Oups, execlp failed.  Exiting\n"); /* This will be read by the  parent. */
    exit(-1); /* To avoid problem if execlp fails, especially if in a loop. */
  } else {
    close(pfd[1]);

    n = read(pfd[0], str, 1000); /* Should be done in a loop until read return 0, but I am lazy. */
    str[n] = '\0';

    close(pfd[0]);
    wait(&n); /* To avoid the zombie process. */

    if (n == 0) {
       printf("%s", str);
    } else {
       printf("Oups, du or execlp failed.\n");
    }
  }
}

即使使用fork和exec代替system,也不建议使用fork。 - AdabH

2
我觉得这个解决方案可能对那些仍然会遇到问题的人很有用。
这是一个函数,用于模拟Linux下的du程序。它递归地遍历所有目录并累加文件大小。
请注意,此函数仍不完整,因为在处理硬链接时会出现错误。应该添加一个容器来存储指向相同inode实体的文件描述符,并使用它来消除多个相同文件的计数。 lstat()用于处理符号链接(又名软链接),但硬链接是一个问题
size_t countDiskUsage(const char* pathname)
{
  if (pathname == NULL) {
    printf("Erorr: pathname is NULL\n");
  }

  struct stat stats;

  if (lstat(pathname, &stats) == 0) {
    if (S_ISREG(stats.st_mode)){
      return stats.st_size;
    }
  } else {
    perror("lstat\n");
  }

  DIR* dir = opendir(pathname);

  if (dir == NULL) {
    perror("Error");
    return 0;
  }

  struct dirent *dirEntry;
  size_t totalSize = 4096;

  for (dirEntry = readdir(dir); dirEntry != NULL; dirEntry =   readdir(dir)) {
    long pathLength = sizeof(char) * (strlen(pathname) + strlen(dirEntry->d_name) + 2);
    char* name = (char*)malloc(pathLength);
    strcpy(name, pathname);
    strcpy(name + strlen(pathname), "/");
    strcpy(name + strlen(pathname) + 1, dirEntry->d_name);

    if (dirEntry->d_type == DT_DIR) {
      if (strcmp(dirEntry->d_name, ".") != 0 && strcmp(dirEntry->d_name, "..") != 0) {
        totalSize += countDiskUsage(name);
      }
    } else {
      int status = lstat(name, &stats);
      if (status == 0) {
        totalSize += stats.st_size;
      } else {
        perror("lstat\n");
      }
    }
    free(name);
  }

  closedir(dir);

  return totalSize;
}

这是一个很好的简单示例。 - Kirubakaran

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