Najszybszym sposobem jest specjalny program, taki jak ten:
#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;
}
Z moich testów bez względu na pamięć podręczną, uruchomiłem każdy z nich około 50 razy w tym samym katalogu, w kółko, aby uniknąć wypaczenia danych w pamięci podręcznej, i otrzymałem z grubsza następujące dane dotyczące wydajności (w czasie rzeczywistym):
ls -1 | wc - 0:01.67
ls -f1 | wc - 0:00.14
find | wc - 0:00.22
dircnt | wc - 0:00.04
Ten ostatni dircnt
to program skompilowany z powyższego źródła.
EDYCJA 2016-09-26
Ze względu na powszechne zapotrzebowanie ponownie napisałem ten program tak, aby był rekurencyjny, więc będzie spadał do podkatalogów i nadal będzie oddzielnie liczył pliki i katalogi.
Ponieważ jest jasne, że niektórzy ludzie chcą wiedzieć, jak to wszystko zrobić, mam wiele komentarzy w kodzie, aby spróbować pokazać, co się dzieje. Napisałem to i przetestowałem na 64-bitowym Linuksie, ale powinno działać na każdym systemie zgodnym z POSIX, w tym Microsoft Windows. Raporty o błędach są mile widziane; Z przyjemnością zaktualizuję to, jeśli nie możesz go uruchomić w systemie AIX, OS / 400 lub czymkolwiek.
Jak widać, jest to o wiele bardziej skomplikowane niż oryginał i koniecznie tak: przynajmniej jedna funkcja musi istnieć, aby była wywoływana rekurencyjnie, chyba że chcesz, aby kod stał się bardzo złożony (np. Zarządzanie stosem podkatalogów i przetwarzanie go w pojedynczej pętli). Ponieważ musimy sprawdzać typy plików, w grę wchodzą różnice między różnymi systemami operacyjnymi, standardowymi bibliotekami itp., Dlatego napisałem program, który stara się być użyteczny w każdym systemie, w którym będzie się kompilował.
Jest bardzo mało sprawdzania błędów, a count
sama funkcja tak naprawdę nie raportuje błędów. Jedyne wywołania, które naprawdę mogą zawieść, to opendir
i stat
(jeśli nie masz szczęścia i masz system, w którym już dirent
zawiera typ pliku). Nie mam paranoi na punkcie sprawdzania całkowitej długości nazw ścieżek podkatalogu, ale teoretycznie system nie powinien zezwalać na żadną nazwę ścieżki dłuższą niż PATH_MAX
. Jeśli są jakieś obawy, mogę to naprawić, ale jest to po prostu więcej kodu, które trzeba wyjaśnić osobie uczącej się pisać C. Ten program ma być przykładem tego, jak rekursywnie zagłębiać się w podkatalogi.
#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;
}
EDYCJA 2017-01-17
Wprowadziłem dwie zmiany sugerowane przez @FlyingCodeMonkey:
- Użyj
lstat
zamiast stat
. Zmieni to zachowanie programu, jeśli w skanowanym katalogu znajdują się dowiązane symbolicznie katalogi. Poprzednie zachowanie polegało na tym, że do (połączonego) podkatalogu dodawano liczbę plików do ogólnej liczby; nowe zachowanie polega na tym, że połączony katalog będzie liczony jako pojedynczy plik, a jego zawartość nie będzie liczona.
- Jeśli ścieżka do pliku jest zbyt długa, zostanie wyemitowany komunikat o błędzie i program zatrzyma się.
EDYCJA 2017-06-29
Przy odrobinie szczęścia będzie to ostatnia edycja tej odpowiedzi :)
Skopiowałem ten kod do repozytorium GitHub, aby nieco łatwiej było uzyskać kod (zamiast kopiować / wklejać, możesz po prostu pobrać źródło ), a ponadto ułatwia każdemu zaproponowanie modyfikacji poprzez przesłanie pull -request z GitHub.
Źródło jest dostępne na licencji Apache License 2.0. Łatki * mile widziane!
- „patch” jest tym, co starzy ludzie tacy jak ja nazywają „pull request”.