Le moyen le plus rapide est un programme spécialement conçu, comme celui-ci:
#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;
}
De mes tests sans égard au cache, j'ai exécuté chacun de ces environ 50 fois chacun sur le même répertoire, encore et encore, pour éviter le biais des données basées sur le cache, et j'ai obtenu à peu près les chiffres de performance suivants (en temps réel):
ls -1 | wc - 0:01.67
ls -f1 | wc - 0:00.14
find | wc - 0:00.22
dircnt | wc - 0:00.04
Ce dernier,, dircnt
est le programme compilé à partir de la source ci-dessus.
MODIFIER 2016-09-26
En raison de la demande générale, j'ai réécrit ce programme pour qu'il soit récursif, il tombera donc dans les sous-répertoires et continuera à compter les fichiers et les répertoires séparément.
Comme il est clair que certaines personnes veulent savoir comment faire tout cela, j'ai beaucoup de commentaires dans le code pour essayer de rendre évident ce qui se passe. Je l'ai écrit et testé sur Linux 64 bits, mais cela devrait fonctionner sur n'importe quel système compatible POSIX, y compris Microsoft Windows. Les rapports de bogue sont les bienvenus; Je suis heureux de mettre à jour ceci si vous ne pouvez pas le faire fonctionner sur votre AIX ou OS / 400 ou autre.
Comme vous pouvez le voir, c'est beaucoup plus compliqué que l'original et forcément: au moins une fonction doit exister pour être appelée de manière récursive à moins que vous ne souhaitiez que le code devienne très complexe (par exemple gérer une pile de sous-répertoires et traiter cela en une seule boucle). Comme nous devons vérifier les types de fichiers, les différences entre les différents systèmes d'exploitation, les bibliothèques standard, etc. entrent en jeu, j'ai donc écrit un programme qui essaie d'être utilisable sur n'importe quel système où il compilera.
Il y a très peu de vérification des erreurs et la count
fonction elle-même ne signale pas vraiment les erreurs. Les seuls appels qui peuvent vraiment échouer sont opendir
et stat
(si vous n'avez pas de chance et que votre système dirent
contient déjà le type de fichier). Je ne suis pas paranoïaque à propos de la vérification de la longueur totale des chemins des sous-répertoires, mais théoriquement, le système ne devrait autoriser aucun nom de chemin plus long que PATH_MAX
. S'il y a des soucis, je peux résoudre ça, mais c'est juste plus de code qui doit être expliqué à quelqu'un qui apprend à écrire C. Ce programme est destiné à être un exemple de la façon de plonger dans les sous-répertoires de manière récursive.
#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;
}
MODIFIER 2017-01-17
J'ai incorporé deux changements suggérés par @FlyingCodeMonkey:
- Utilisez à la
lstat
place de stat
. Cela changera le comportement du programme si vous avez des répertoires liés par un lien symbolique dans le répertoire que vous analysez. Le comportement précédent était que le sous-répertoire (lié) verrait son nombre de fichiers ajouté au nombre global; le nouveau comportement est que le répertoire lié comptera comme un seul fichier et que son contenu ne sera pas compté.
- Si le chemin d'un fichier est trop long, un message d'erreur sera émis et le programme s'arrêtera.
MODIFIER 2017-06-29
Avec un peu de chance, ce sera la dernière modification de cette réponse :)
J'ai copié ce code dans un référentiel GitHub pour faciliter l'obtention du code (au lieu de copier / coller, vous pouvez simplement télécharger la source ), et il est plus facile pour quiconque de suggérer une modification en soumettant un tirage -request de GitHub.
La source est disponible sous Apache License 2.0. Patchs * bienvenus!
- "patch" est ce que les personnes âgées comme moi appellent une "pull request".