Der schnellste Weg ist ein speziell entwickeltes Programm wie das folgende:
#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;
}
Bei meinen Tests ohne Rücksicht auf den Cache habe ich jedes dieser Elemente ungefähr 50 Mal für dasselbe Verzeichnis ausgeführt, um einen cachebasierten Datenversatz zu vermeiden, und ich habe ungefähr die folgenden Leistungszahlen (in Echtzeit) erhalten:
ls -1 | wc - 0:01.67
ls -f1 | wc - 0:00.14
find | wc - 0:00.22
dircnt | wc - 0:00.04
Das letzte dircnt
ist das Programm, das aus der obigen Quelle zusammengestellt wurde.
EDIT 2016-09-26
Aufgrund der großen Nachfrage habe ich dieses Programm so umgeschrieben, dass es rekursiv ist, sodass es in Unterverzeichnisse verschoben wird und weiterhin Dateien und Verzeichnisse separat zählt.
Da es klar ist, dass einige Leute wissen wollen, wie man das alles macht, habe ich viele Kommentare im Code, um zu verdeutlichen, was los ist. Ich habe dies geschrieben und unter 64-Bit-Linux getestet, aber es sollte auf jedem POSIX-kompatiblen System funktionieren, einschließlich Microsoft Windows. Fehlerberichte sind willkommen. Ich freue mich, dies zu aktualisieren, wenn Sie es unter AIX oder OS / 400 oder was auch immer nicht zum Laufen bringen können.
Wie Sie sehen, ist es viel komplizierter als das Original und dies muss auch so sein: Es muss mindestens eine Funktion vorhanden sein, um rekursiv aufgerufen zu werden, es sei denn, Sie möchten, dass der Code sehr komplex wird (z. B. Verwalten eines Unterverzeichnisstapels und Verarbeiten dieser in einer einzelnen Schleife). Da wir Dateitypen überprüfen müssen, kommen Unterschiede zwischen verschiedenen Betriebssystemen, Standardbibliotheken usw. ins Spiel. Deshalb habe ich ein Programm geschrieben, das versucht, auf jedem System verwendet werden zu können, auf dem es kompiliert wird.
Es gibt nur sehr wenige Fehlerprüfungen und die count
Funktion selbst meldet keine Fehler. Die einzigen Aufrufe, die wirklich fehlschlagen können, sind opendir
und stat
(wenn Sie kein Glück haben und ein System haben, in dem dirent
der Dateityp bereits enthalten ist). Ich bin nicht paranoid, wenn es darum geht, die Gesamtlänge der Subdir-Pfadnamen zu überprüfen, aber theoretisch sollte das System keinen Pfadnamen zulassen, der länger als ist PATH_MAX
. Wenn es Bedenken gibt, kann ich das beheben, aber es ist nur mehr Code, der jemandem erklärt werden muss, der das Schreiben von C lernt. Dieses Programm soll ein Beispiel dafür sein, wie man rekursiv in Unterverzeichnisse eintaucht.
#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
Ich habe zwei von @FlyingCodeMonkey vorgeschlagene Änderungen vorgenommen:
- Verwenden Sie
lstat
anstelle von stat
. Dies ändert das Verhalten des Programms, wenn Sie Verzeichnisse in dem Verzeichnis, das Sie scannen, mit Symlinks verknüpft haben. Das vorherige Verhalten war, dass die Anzahl der Dateien im (verknüpften) Unterverzeichnis zur Gesamtzahl hinzugefügt wurde. Das neue Verhalten ist, dass das verknüpfte Verzeichnis als einzelne Datei zählt und sein Inhalt nicht gezählt wird.
- Wenn der Pfad einer Datei zu lang ist, wird eine Fehlermeldung ausgegeben und das Programm angehalten.
EDIT 2017-06-29
Mit etwas Glück ist dies die letzte Bearbeitung dieser Antwort :)
Ich habe diesen Code in ein GitHub-Repository kopiert , um das Abrufen des Codes zu vereinfachen (anstelle des Kopierens / Einfügens können Sie einfach die Quelle herunterladen ). Außerdem kann jeder leichter eine Änderung vorschlagen, indem er einen Pull sendet -Anfrage von GitHub.
Die Quelle ist unter Apache License 2.0 verfügbar. Patches * willkommen!
- "Patch" nennen alte Leute wie ich eine "Pull-Anfrage".