Самый быстрый способ - это специальная программа, например:
#include <stdio.h>
#include <dirent.h>
int main(int argc, char *argv[]) {
DIR *dir;
struct dirent *ent;
long count = 0;
dir = opendir(argv[1]);
while((ent = readdir(dir)))
++count;
closedir(dir);
printf("%s contains %ld files\n", argv[1], count);
return 0;
}
Из моего тестирования без учета кеширования я запускал каждый из них примерно по 50 раз в одном и том же каталоге, снова и снова, чтобы избежать перекоса данных на основе кеша, и я получил примерно следующие показатели производительности (в реальном времени):
ls -1 | wc - 0:01.67
ls -f1 | wc - 0:00.14
find | wc - 0:00.22
dircnt | wc - 0:00.04
Последняя dircnt
, это программа, скомпилированная из вышеуказанного источника.
РЕДАКТИРОВАТЬ 2016-09-26
По многочисленным просьбам я переписал эту программу, сделав ее рекурсивной, чтобы она переместилась в подкаталоги и продолжала отдельно подсчитывать файлы и каталоги.
Поскольку ясно, что некоторые люди хотят знать, как все это сделать, у меня есть много комментариев в коде, чтобы попытаться понять, что происходит. Я написал это и протестировал на 64-битном Linux, но он должен работать на любой POSIX-совместимой системе, включая Microsoft Windows. Сообщения об ошибках приветствуются; Я рад обновить это, если вы не можете заставить его работать на вашем AIX или OS / 400 или чем-то еще.
Как видите, это много сложнее, чем оригинал, и обязательно так: должна существовать по крайней мере одна функция для рекурсивного вызова, если вы не хотите, чтобы код стал очень сложным (например, управление стеком подкаталогов и обработка его в одном цикле). Поскольку мы должны проверять типы файлов, в игру вступают различия между различными ОС, стандартными библиотеками и т. Д., Поэтому я написал программу, которая пытается работать в любой системе, где она будет компилироваться.
Проверки ошибок очень мало, и сама count
функция на самом деле не сообщает об ошибках. Единственные вызовы, которые могут действительно потерпеть неудачу, это opendir
и stat
(если вам не повезло, и у вас есть система, в которой уже dirent
есть тип файла). Я не параноик по поводу проверки общей длины имен пути subdir, но теоретически система не должна разрешать любое имя пути длиннее, чем PATH_MAX
. Если есть сомнения, я могу это исправить, но это просто дополнительный код, который нужно объяснить тому, кто учится писать C. Эта программа предназначена для того, чтобы быть примером того, как рекурсивно погружаться в подкаталоги.
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <sys/stat.h>
#if defined(WIN32) || defined(_WIN32)
#define PATH_SEPARATOR '\\'
#else
#define PATH_SEPARATOR '/'
#endif
/* A custom structure to hold separate file and directory counts */
struct filecount {
long dirs;
long files;
};
/*
* counts the number of files and directories in the specified directory.
*
* path - relative pathname of a directory whose files should be counted
* counts - pointer to struct containing file/dir counts
*/
void count(char *path, struct filecount *counts) {
DIR *dir; /* dir structure we are reading */
struct dirent *ent; /* directory entry currently being processed */
char subpath[PATH_MAX]; /* buffer for building complete subdir and file names */
/* Some systems don't have dirent.d_type field; we'll have to use stat() instead */
#if !defined ( _DIRENT_HAVE_D_TYPE )
struct stat statbuf; /* buffer for stat() info */
#endif
/* fprintf(stderr, "Opening dir %s\n", path); */
dir = opendir(path);
/* opendir failed... file likely doesn't exist or isn't a directory */
if(NULL == dir) {
perror(path);
return;
}
while((ent = readdir(dir))) {
if (strlen(path) + 1 + strlen(ent->d_name) > PATH_MAX) {
fprintf(stdout, "path too long (%ld) %s%c%s", (strlen(path) + 1 + strlen(ent->d_name)), path, PATH_SEPARATOR, ent->d_name);
return;
}
/* Use dirent.d_type if present, otherwise use stat() */
#if defined ( _DIRENT_HAVE_D_TYPE )
/* fprintf(stderr, "Using dirent.d_type\n"); */
if(DT_DIR == ent->d_type) {
#else
/* fprintf(stderr, "Don't have dirent.d_type, falling back to using stat()\n"); */
sprintf(subpath, "%s%c%s", path, PATH_SEPARATOR, ent->d_name);
if(lstat(subpath, &statbuf)) {
perror(subpath);
return;
}
if(S_ISDIR(statbuf.st_mode)) {
#endif
/* Skip "." and ".." directory entries... they are not "real" directories */
if(0 == strcmp("..", ent->d_name) || 0 == strcmp(".", ent->d_name)) {
/* fprintf(stderr, "This is %s, skipping\n", ent->d_name); */
} else {
sprintf(subpath, "%s%c%s", path, PATH_SEPARATOR, ent->d_name);
counts->dirs++;
count(subpath, counts);
}
} else {
counts->files++;
}
}
/* fprintf(stderr, "Closing dir %s\n", path); */
closedir(dir);
}
int main(int argc, char *argv[]) {
struct filecount counts;
counts.files = 0;
counts.dirs = 0;
count(argv[1], &counts);
/* If we found nothing, this is probably an error which has already been printed */
if(0 < counts.files || 0 < counts.dirs) {
printf("%s contains %ld files and %ld directories\n", argv[1], counts.files, counts.dirs);
}
return 0;
}
РЕДАКТИРОВАТЬ 2017-01-17
Я включил два изменения, предложенные @FlyingCodeMonkey:
- Используйте
lstat
вместо stat
. Это изменит поведение программы, если у вас есть каталоги с символическими ссылками в каталоге, который вы сканируете. Предыдущее поведение состояло в том, что (связанный) подкаталог будет иметь свое количество файлов, добавленное к общему количеству; новое поведение заключается в том, что связанный каталог будет учитываться как один файл, а его содержимое не будет учитываться.
- Если путь к файлу слишком длинный, выдается сообщение об ошибке и программа останавливается.
РЕДАКТИРОВАТЬ 2017-06-29
Если повезет, это будет последняя редакция этого ответа :)
Я скопировал этот код в репозиторий GitHub, чтобы немного упростить получение кода (вместо копирования / вставки вы можете просто загрузить исходный код ), плюс он позволяет любому предложить модификацию, отправив извлечение -запрос от GitHub.
Исходный код доступен по лицензии Apache License 2.0. Патчи * добро пожаловать!
- «патч» - это то, что старые люди, такие как я, называют «запросом на получение ответа»