Il modo più veloce è un programma appositamente costruito, come questo:
#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;
}
Dai miei test senza riguardo alla cache, ho eseguito ciascuno di questi circa 50 volte ciascuno contro la stessa directory, ripetutamente, per evitare l'inclinazione dei dati basata sulla cache e ho ottenuto approssimativamente i seguenti numeri di prestazioni (in tempo reale):
ls -1 | wc - 0:01.67
ls -f1 | wc - 0:00.14
find | wc - 0:00.22
dircnt | wc - 0:00.04
Quest'ultimo dircnt
, è il programma compilato dalla fonte sopra.
MODIFICA 26/09/2016 2016
A causa della grande richiesta, ho riscritto questo programma in modo ricorsivo, quindi cadrà nelle sottodirectory e continuerà a contare i file e le directory separatamente.
Dal momento che è chiaro che alcune persone vogliono sapere come fare tutto questo, ho molti commenti nel codice per cercare di rendere ovvio cosa sta succedendo. L'ho scritto e testato su Linux a 64 bit, ma dovrebbe funzionare su qualsiasi sistema compatibile con POSIX, incluso Microsoft Windows. Le segnalazioni di bug sono benvenute; Sono felice di aggiornarlo se non riesci a farlo funzionare su AIX o OS / 400 o altro.
Come puoi vedere, è molto più complicato dell'originale e necessariamente così: almeno una funzione deve esistere per essere chiamata in modo ricorsivo a meno che tu non voglia che il codice diventi molto complesso (es. Gestire uno stack di sottodirectory ed elaborarlo in un singolo loop). Dal momento che dobbiamo verificare i tipi di file, entrano in gioco le differenze tra diversi sistemi operativi, librerie standard, ecc., Quindi ho scritto un programma che cerca di essere utilizzabile su qualsiasi sistema in cui verrà compilato.
Il controllo degli errori è molto limitato e la count
funzione stessa non segnala errori. Le uniche chiamate che possono davvero fallire sono opendir
e stat
(se non sei fortunato e hai un sistema in cui dirent
contiene già il tipo di file). Non sono paranoico nel controllare la lunghezza totale dei nomi dei percorsi dei subdir, ma teoricamente il sistema non dovrebbe consentire alcun nome di percorso più lungo di PATH_MAX
. Se ci sono preoccupazioni, posso risolverlo, ma è solo più codice che deve essere spiegato a qualcuno che sta imparando a scrivere C. Questo programma vuole essere un esempio di come immergersi in sottodirectory ricorsivamente.
#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;
}
MODIFICA 2017-01-17
Ho incorporato due modifiche suggerite da @FlyingCodeMonkey:
- Usa
lstat
invece di stat
. Ciò cambierà il comportamento del programma se nella directory che si sta eseguendo la scansione sono presenti directory con collegamenti simbolici. Il comportamento precedente era che la sottodirectory (collegata) avrebbe avuto il suo conteggio dei file aggiunto al conteggio complessivo; il nuovo comportamento è che la directory collegata verrà conteggiata come un singolo file e il suo contenuto non verrà conteggiato.
- Se il percorso di un file è troppo lungo, verrà emesso un messaggio di errore e il programma si arresterà.
MODIFICA 2017-06-29
Con un po 'di fortuna, questa sarà l' ultima modifica di questa risposta :)
Ho copiato questo codice in un repository GitHub per rendere un po 'più semplice ottenere il codice (invece di copiare / incollare, puoi semplicemente scaricare il sorgente ), inoltre rende più semplice per chiunque suggerire una modifica inviando un pull -Richiesta da GitHub.
La fonte è disponibile con licenza Apache 2.0. Patch * benvenuto!
- "patch" è ciò che gli anziani come me chiamano "richiesta pull".