как программно получить размер каталога в Linux?

Я хочу получить точный размер определенного каталога в Linux с помощью программы C. Я пытался использовать statfs(path,struct statfs &), но он не дает точного размера. Я также пытался использовать stat(), но он возвращает размер 4096 для любого каталога!

Пожалуйста, предложите мне способ, с помощью которого я могу получить точный размер каталога, как мы получаем после команды «du -sh dirPath».

Также я не хочу использовать du через system().

Заранее спасибо.


person Community    schedule 15.07.2009    source источник
comment
Использование диска (du) и сумма размеров файлов (stat) — не одно и то же. Какой ты хочешь?   -  person Randy Proctor    schedule 15.07.2009
comment
stat для каталога не возвращает сумму размеров файлов. stat для каталога возвращает объем пространства, используемого самой записью каталога.   -  person derobert    schedule 15.07.2009


Ответы (4)


Типичное решение

Если вам нужен размер каталога, похожий по моде на du, создайте рекурсивную функцию. Можно решить проблему итеративно, но решение поддается рекурсии.

Информация

Вот ссылка для начала:

http://www.cs.utk.edu/~plank/plank/classes/cs360/360/notes/Prsize/lecture.html

Поиск

Поиск в Google с помощью 'stat c program recursive directory размер'

Пример

Непосредственно с веб-сайта Джима Планка в качестве пример для начала.

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

main()
{
  DIR *d;
  struct dirent *de;
  struct stat buf;
  int exists;
  int total_size;

  d = opendir(".");
  if (d == NULL) {
    perror("prsize");
    exit(1);
  }

  total_size = 0;

  for (de = readdir(d); de != NULL; de = readdir(d)) {
    exists = stat(de->d_name, &buf);
    if (exists < 0) {
      fprintf(stderr, "Couldn't stat %s\n", de->d_name);
    } else {
      total_size += buf.st_size;
    }
  }
  closedir(d);
  printf("%d\n", total_size);
}
person Dave Jarvis    schedule 15.07.2009
comment
Обратите внимание, что de-›d_name будет содержать только имя узла внутри каталога, а не абсолютный путь. - person Mitch Haile; 02.03.2017
comment
происходит сбой, когда каталог содержит заблокированные файлы - person Rehan; 13.05.2020

Вам нужно stat() все файлы в текущем каталоге и подкаталогах и добавить их.

Рассмотрите возможность использования рекурсивного алгоритма для этого.

person Byron Whitlock    schedule 15.07.2009

Если вы не хотите использовать '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");
    }
  }
}
person jfg956    schedule 21.07.2011

Я думаю, решение может быть полезно для тех, кто все еще может столкнуться с проблемой.

Вот функция, которая была написана для имитации программы 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;
}
person goodmove    schedule 16.05.2017