Hemos elegido lenguaje C para hacer esto, porque en bash podemos llamar a ls o find y apaga y vámonos.
Estas funciones, nos recordarán al uso de archivos con fopen() y fclose().
Para usar esta implementación, como veremos en el ejemplo, debemos incluir <sys/types.c> y <dirent.h> y, en principio, empezaremos con un ejemplo sencillo que lista los archivos del directorio actual (una vez tenemos el nombre y la ruta del archivo podemos hacer con él lo que queramos):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | #include <stdlib.h> #include <stdio.h> #include <sys/types.h> #include <dirent.h> /* Función para devolver un error en caso de que ocurra */ void error(const char *s); /* Función que hace algo con un archivo */ void procesoArchivo(char *archivo); int main() { /* Con un puntero a DIR abriremos el directorio */ DIR *dir; /* en *ent habrá información sobre el archivo que se está "sacando" a cada momento */ struct dirent *ent; /* Empezaremos a leer en el directorio actual */ dir = opendir ("."); /* Miramos que no haya error */ if (dir == NULL) error("No puedo abrir el directorio"); /* Una vez nos aseguramos de que no hay error, ¡vamos a jugar! */ /* Leyendo uno a uno todos los archivos que hay */ while ((ent = readdir (dir)) != NULL) { /* Nos devolverá el directorio actual (.) y el anterior (..), como hace ls */ if ( (strcmp(ent->d_name, ".")!=0) && (strcmp(ent->d_name, "..")!=0) ) { /* Una vez tenemos el archivo, lo pasamos a una función para procesarlo. */ procesoArchivo(ent->d_name); } } closedir (dir); return EXIT_SUCCESS; } void error(const char *s) { /* perror() devuelve la cadena S y el error (en cadena de caracteres) que tenga errno */ perror (s); exit(EXIT_FAILURE); } void procesoArchivo(char *archivo) { /* Para "procesar", o al menos, hacer algo con el archivo, vamos a decir su tamaño en bytes */ /* para ello haremos lo que vemos aquí: https://poesiabinaria.net/2010/04/tamano-de-un-fichero-en-c/ */ FILE *fich; long ftam; fich=fopen(archivo, "r"); if (fich) { fseek(fich, 0L, SEEK_END); ftam=ftell(fich); fclose(fich); /* Si todo va bien, decimos el tamaño */ printf ("%30s (%ld bytes)\n", archivo, ftam); } else /* Si ha pasado algo, sólo decimos el nombre */ printf ("%30s (No info.)\n", archivo); } |
Este es un ejemplo sencillo, podemos profundizar un poco más. Vemos que readdir() nos devuelve todo lo que hay, y a veces, es muy útil diferenciar entre un fichero, un directorio, un enlace, un socket, un fifo, etc… para eso, en un principio utilizaremos la información del mismo dirent, que a veces la encontramos de forma rápida, aunque existen sistemas de archivos que no nos facilitan esa información de forma tan inmediata y tenemos que hacer stat(), un ejemplo de ello es nfs, ya que es significativamente más lento obtener esa información, si no la necesitamos, no intenta obtenerla, es para esos casos cuando obligamos a su obtención con stat().
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | #include <stdlib.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <dirent.h> #include <string.h> #include <errno.h> /* Función para devolver un error en caso de que ocurra */ void error(const char *s); /* Calculamos el tamaño del archivo */ long fileSize(char *fname); /* Sacamos el tipo de archivo haciendo un stat(), es como el stat de la línea de comandos */ unsigned char statFileType(char *fname); /* Función que hace algo con un archivo, pero le pasamos el dirent completo, usaremos más datos */ void procesoArchivo(char *ruta, struct dirent *ent); int main(int argc, char *argv[]) { /* Con un puntero a DIR abriremos el directorio */ DIR *dir; /* en *ent habrá información sobre el archivo que se está "sacando" a cada momento */ struct dirent *ent; if (argc != 2) { error("Uso: ./directorio_2 <ruta>\n"); } /* Empezaremos a leer en el directorio actual */ dir = opendir (argv[1]); /* Miramos que no haya error */ if (dir == NULL) error("No puedo abrir el directorio"); /* Una vez nos aseguramos de que no hay error, ¡vamos a jugar! */ /* Leyendo uno a uno todos los archivos que hay */ while ((ent = readdir (dir)) != NULL) { /* Nos devolverá el directorio actual (.) y el anterior (..), como hace ls */ if ( (strcmp(ent->d_name, ".")!=0) && (strcmp(ent->d_name, "..")!=0) ) { /* Una vez tenemos el archivo, lo pasamos a una función para procesarlo. */ procesoArchivo(argv[1], ent); } } closedir (dir); return EXIT_SUCCESS; } void error(const char *s) { /* perror() devuelve la cadena S y el error (en cadena de caracteres) que tenga errno */ perror (s); exit(EXIT_FAILURE); } long fileSize(char *fname) { FILE *fich; long ftam=-1; fich=fopen(fname, "r"); if (fich) { fseek(fich, 0L, SEEK_END); ftam=ftell(fich); fclose(fich); } else printf("ERRNO: %d - %s\n", errno, strerror(errno)); return ftam; } void procesoArchivo(char *ruta, struct dirent *ent) { long ftam; char *nombrecompleto; char strtam[20]; char strtipo[30]=""; /* Tiene que ser del mismo tipo de dirent.d_type en nuestro sistema */ static unsigned char tipoID[7]={DT_BLK, DT_CHR, DT_DIR, DT_FIFO, DT_LNK, DT_REG, DT_SOCK}; static char* tipoSTRs[7]={"Dispositivo de bloques", "Dispositivo de caracteres", "Directorio", "FIFO", "Enlace", "Archivo regular", "Socket Unix"}; int i; int tmp; unsigned char tipo; /* Sacamos el nombre completo con la ruta del archivo */ tmp=strlen(ruta); nombrecompleto=malloc(tmp+strlen(ent->d_name)+2); /* Sumamos 2, por el \0 y la barra de directorios (/) no sabemos si falta */ if (ruta[tmp-1]=='/') sprintf(nombrecompleto,"%s%s", ruta, ent->d_name); else sprintf(nombrecompleto,"%s/%s", ruta, ent->d_name); /* Calcula el tamaño */ ftam=fileSize(nombrecompleto); if (ftam>=0) sprintf(strtam, "%ld bytes", ftam); else strcpy(strtam, "No info"); /* A veces ent->d_type no nos dice nada, eso depende del sistema de archivos que estemos */ /* mirando, por ejemplo ext*, brtfs, sí nos dan esta información. Por el contrario, nfs */ /* no nos la da (directamente, una vez que hacemos stat sí lo hace), y es en estos casos donde probamos con stat() */ tipo=ent->d_type; if (tipo==DT_UNKNOWN) tipo=statFileType(nombrecompleto); if (tipo!=DT_UNKNOWN) { /* Podíamos haber hecho un switch con los tipos y devolver la cadena, pero me da la impresión de que así es menos costoso de escribir. */ i=0; while ( (i<7) && (tipo!=tipoID[i]) ) ++i; if (i<7) strcpy(strtipo, tipoSTRs[i]); } /* Si no hemos encontrado el tipo, éste será desconocido */ if (strtipo[0]=='\0') strcpy(strtipo, "Tipo desconocido"); printf ("%30s (%s)\t%s \n", ent->d_name, strtam, strtipo); free(nombrecompleto); } /* stat() vale para mucho más, pero sólo queremos el tipo ahora */ unsigned char statFileType(char *fname) { struct stat sdata; /* Intentamos el stat() si no funciona, devolvemos tipo desconocido */ if (stat(fname, &sdata)==-1) { return DT_UNKNOWN; } switch (sdata.st_mode & S_IFMT) { case S_IFBLK: return DT_BLK; case S_IFCHR: return DT_CHR; case S_IFDIR: return DT_DIR; case S_IFIFO: return DT_FIFO; case S_IFLNK: return DT_LNK; case S_IFREG: return DT_REG; case S_IFSOCK: return DT_SOCK; default: return DT_UNKNOWN; } } |
Un ejemplo más grande, contamos los archivos que hay en un árbol de directorios. Uno a uno, pero de forma recursiva (cuando encontramos un directorio nos meteremos dentro y llamaremos de nuevo a la función principal (cuentaArchivos()):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | #include <stdlib.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <dirent.h> #include <string.h> #include <errno.h> /* Función para devolver un error en caso de que ocurra */ void error(const char *s); /* Calculamos el tamaño del archivo */ long fileSize(char *fname); /* Sacamos el tipo de archivo haciendo un stat(), es como el stat de la línea de comandos */ unsigned char statFileType(char *fname); /* Intenta sacar el tipo de archivo del ent */ unsigned char getFileType(char *ruta, struct dirent *ent); /* Obtiene el nombre del fichero con la ruta completa */ char *getFullName(char *ruta, struct dirent *ent); /* Genera una cadena de espacios, para dibujar el árbol */ char *generaPosStr(int niv); /* Función principal, que cuenta archivos */ unsigned cuentaArchivos(char *ruta, int niv); int main(int argc, char *argv[]) { unsigned num; if (argc != 2) { error("Uso: ./directorio_2 <ruta>\n"); } printf("Entrando en: %s\n", argv[1]); num=cuentaArchivos(argv[1], 1); printf("%s . Total: %u archivos\n", argv[1], num); /* Empezaremos a leer en el directorio actual */ return EXIT_SUCCESS; } void error(const char *s) { /* perror() devuelve la cadena S y el error (en cadena de caracteres) que tenga errno */ perror (s); exit(EXIT_FAILURE); } char *getFullName(char *ruta, struct dirent *ent) { char *nombrecompleto; int tmp; tmp=strlen(ruta); nombrecompleto=malloc(tmp+strlen(ent->d_name)+2); /* Sumamos 2, por el \0 y la barra de directorios (/) no sabemos si falta */ if (ruta[tmp-1]=='/') sprintf(nombrecompleto,"%s%s", ruta, ent->d_name); else sprintf(nombrecompleto,"%s/%s", ruta, ent->d_name); return nombrecompleto; } char *generaPosStr(int niv) { int i; char *tmp=malloc(niv*2+1); /* Dos espacios por nivel más terminador */ for (i=0; i<niv*2; ++i) tmp[i]=' '; tmp[niv*2]='\0'; return tmp; } unsigned cuentaArchivos(char *ruta, int niv) { /* Con un puntero a DIR abriremos el directorio */ DIR *dir; /* en *ent habrá información sobre el archivo que se está "sacando" a cada momento */ struct dirent *ent; unsigned numfiles=0; /* Ficheros en el directorio actual */ unsigned char tipo; /* Tipo: fichero /directorio/enlace/etc */ char *nombrecompleto; /* Nombre completo del fichero */ char *posstr; /* Cadena usada para posicionarnos horizontalmente */ dir = opendir (ruta); /* Miramos que no haya error */ if (dir == NULL) error("No puedo abrir el directorio"); while ((ent = readdir (dir)) != NULL) { if ( (strcmp(ent->d_name, ".")!=0) && (strcmp(ent->d_name, "..")!=0) ) { nombrecompleto=getFullName(ruta, ent); tipo=getFileType(nombrecompleto, ent); if (tipo==DT_REG) { ++numfiles; } else if (tipo==DT_DIR) { posstr=generaPosStr(niv); printf("%sEntrando en: %s\n", posstr, nombrecompleto); printf("%s%s . Total: %u archivos ", posstr, nombrecompleto, cuentaArchivos(nombrecompleto, niv+1)); /* Podemos poner las líneas que queramos */ printf("\n"); free(posstr); } free(nombrecompleto); } } closedir (dir); return numfiles; } unsigned char getFileType(char *nombre, struct dirent *ent) { unsigned char tipo; tipo=ent->d_type; if (tipo==DT_UNKNOWN) { tipo=statFileType(nombre); } return tipo; } /* stat() vale para mucho más, pero sólo queremos el tipo ahora */ unsigned char statFileType(char *fname) { struct stat sdata; /* Intentamos el stat() si no funciona, devolvemos tipo desconocido */ if (stat(fname, &sdata)==-1) { return DT_UNKNOWN; } switch (sdata.st_mode & S_IFMT) { case S_IFBLK: return DT_BLK; case S_IFCHR: return DT_CHR; case S_IFDIR: return DT_DIR; case S_IFIFO: return DT_FIFO; case S_IFLNK: return DT_LNK; case S_IFREG: return DT_REG; case S_IFSOCK: return DT_SOCK; default: return DT_UNKNOWN; } } |
Se puede descargar todo el código fuente desde aquí: Listado de archivos dentro de un directorio en C (3.5Kb)
Actualización (14/10/2011): En el blog código para llevar [el blog ya no está disponible] encontramos un ejemplo de cómo utilizar este código para encontrar el número de núcleos de nuestra CPU.