La forma más rápida es un programa especialmente diseñado, como este:
#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 mis pruebas sin tener en cuenta el caché, ejecuté cada una de estas aproximadamente 50 veces cada una contra el mismo directorio, una y otra vez, para evitar el sesgo de datos basado en caché, y obtuve aproximadamente los siguientes números de rendimiento (en tiempo de reloj real):
ls -1 | wc - 0:01.67
ls -f1 | wc - 0:00.14
find | wc - 0:00.22
dircnt | wc - 0:00.04
Ese último dircnt
, es el programa compilado de la fuente anterior.
EDITAR 2016-09-26
Debido a la demanda popular, he reescrito este programa para que sea recursivo, por lo que caerá en subdirectorios y continuará contando archivos y directorios por separado.
Como está claro que algunas personas quieren saber cómo hacer todo esto, tengo muchos comentarios en el código para tratar de hacer obvio lo que está sucediendo. Escribí esto y lo probé en Linux de 64 bits, pero debería funcionar en cualquier sistema compatible con POSIX, incluido Microsoft Windows. Los informes de errores son bienvenidos; Me complace actualizar esto si no puede hacerlo funcionar en su AIX o OS / 400 o lo que sea.
Como puede ver, es mucho más complicado que el original y necesariamente así: debe existir al menos una función para que se invoque de forma recursiva a menos que desee que el código se vuelva muy complejo (por ejemplo, administrar una pila de subdirectorios y procesarla en un solo bucle). Como tenemos que verificar los tipos de archivos, entran en juego las diferencias entre diferentes sistemas operativos, bibliotecas estándar, etc., por lo que he escrito un programa que intenta ser utilizable en cualquier sistema donde se compilará.
Hay muy poca comprobación de errores, y la count
función en sí misma no informa errores. Las únicas llamadas que realmente pueden fallar son opendir
y stat
(si no tienes suerte y tienes un sistema que ya dirent
contiene el tipo de archivo). No estoy paranoico acerca de verificar la longitud total de los nombres de ruta de subdirección, pero en teoría, el sistema no debería permitir ningún nombre de ruta que sea más largo que PATH_MAX
. Si hay dudas, puedo solucionarlo, pero es solo más código que debe explicarse a alguien que está aprendiendo a escribir C. Este programa pretende ser un ejemplo de cómo sumergirse en subdirectorios de forma recursiva.
#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;
}
EDITAR 2017-01-17
He incorporado dos cambios sugeridos por @FlyingCodeMonkey:
- Usar en
lstat
lugar de stat
. Esto cambiará el comportamiento del programa si tiene directorios enlazados en el directorio que está escaneando. El comportamiento anterior era que el subdirectorio (vinculado) tendría su recuento de archivos agregado al recuento general; El nuevo comportamiento es que el directorio vinculado contará como un solo archivo y no se contará su contenido.
- Si la ruta de un archivo es demasiado larga, se emitirá un mensaje de error y el programa se detendrá.
EDITAR 2017-06-29
Con suerte, esta será la última edición de esta respuesta :)
He copiado este código en un repositorio de GitHub para que sea un poco más fácil obtener el código (en lugar de copiar / pegar, solo puede descargar la fuente ), además hace que sea más fácil para cualquier persona sugerir una modificación enviando un pull -solicitud de GitHub.
La fuente está disponible bajo la licencia Apache 2.0. Parches * bienvenido!
- "parche" es lo que las personas mayores como yo llaman una "solicitud de extracción".