Найшвидший спосіб - це цілеспрямована програма, наприклад:
#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
, - це програма, складена з вищевказаного джерела.
EDIT 2016-09-26
Зважаючи на популярність, я переписав цю програму на рекурсивну, тому вона потрапить у підкаталоги та продовжуватиме рахувати файли та каталоги окремо.
Оскільки зрозуміло, що деякі люди хочуть знати, як все це зробити, у мене є багато коментарів у коді, щоб спробувати зробити це очевидним, що відбувається. Я написав це і протестував його на 64-бітному Linux, але він повинен працювати в будь-якій системі, сумісній з POSIX, включаючи Microsoft Windows. Звіти про помилки вітаються; Я радий оновити це, якщо ви не можете працювати з AIX або OS / 400 чи іншим.
Як бачите, це набагато складніше, ніж оригінал і обов'язково так: принаймні одна функція повинна існувати, щоб викликати рекурсивно, якщо ви не хочете, щоб код став дуже складним (наприклад, керування стеком підкаталогів та обробка цього в одному циклі). Оскільки ми маємо перевірити типи файлів, відмінності між різними ОС, стандартними бібліотеками тощо вступають у гру, тому я написав програму, яка намагається бути корисною для будь-якої системи, де вона буде компілюватися.
Перевірки помилок дуже мало, а сама count
функція насправді не повідомляє про помилки. Єдині дзвінки, які дійсно можуть бути невдалими, є opendir
і stat
(якщо вам не пощастило та у вас система, де вже dirent
є тип файлу). Я не параноїчний щодо перевірки загальної довжини імен шляхів субдір, але теоретично система не повинна допускати жодного імені шляху, що перевищує ніж 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;
}
EDIT 2017-01-17
Я включив дві зміни, запропоновані @FlyingCodeMonkey:
- Використовуйте
lstat
замість stat
. Це змінить поведінку програми, якщо ви посилаєтесь на каталоги в сканованому каталозі. Попередня поведінка полягала в тому, що (пов'язаний) підкаталог буде додавати до загального підрахунку кількість файлів; нова поведінка полягає в тому, що пов'язаний каталог буде рахуватися як один файл, а його вміст не буде зараховано.
- Якщо шлях файлу занадто довгий, надсилатиметься повідомлення про помилку, і програма зупиняється.
EDIT 2017-06-29
При будь-якій удачі, це буде остання редакція цієї відповіді :)
Я скопіював цей код у сховище GitHub, щоб полегшити отримання коду (замість копіювання / вставки, ви можете просто завантажити джерело ), а також полегшити можливість запропонувати модифікацію, подавши потяг -запит від GitHub.
Джерело доступне під ліцензією Apache License 2.0. Патчі * Ласкаво просимо!
- "латка" - це те, що старі люди, як я, називають "запитом на витяг".