sirven que por para lista ejemplos consola comandos buscar basicos archivos linux shell disk-io

que - en linux



Recuento rápido de archivos de Linux para una gran cantidad de archivos (17)

Estoy tratando de encontrar la mejor manera de encontrar la cantidad de archivos en un directorio en particular cuando hay una gran cantidad de archivos (> 100,000).

Cuando hay tantos archivos, la ejecución de "ls | wc -l" tarda bastante tiempo en ejecutarse. Creo que esto se debe a que devuelve los nombres de todos los archivos. Estoy tratando de tomar la menor cantidad de disco IO posible.

He experimentado con algunos guiones de Shell y Perl sin éxito. ¿Algunas ideas?


Recuento rápido de archivos de Linux

El conteo de archivos de linux más rápido que sé es

locate -c -r ''/home''

¡No hay necesidad de invocar grep! Pero como se mencionó, debería tener una nueva base de datos. Mira hombre localizar

-c, --count Instead of writing file names on standard output, write the number of matching entries only.

¡Además , debe saber que también cuenta los directorios como archivos!

Por cierto: si quieres una visión general de tus archivos y directorios usa

locate -S

Muestra la cantidad de directorios, archivos, etc.


¿Has intentado encontrar? Por ejemplo:

find . -name "*.ext" | wc -l


Deberías usar "getdents" en lugar de ls / find

Aquí hay un artículo muy bueno que describe el enfoque de getdents.

http://be-n.com/spw/you-can-list-a-million-files-in-a-directory-but-not-with-ls.html

Aquí está el extracto:

ls y prácticamente cualquier otro método para listar un directorio (incluyendo python os.listdir, find.) confía en libc readdir (). Sin embargo, readdir () solo lee 32K de entradas de directorio a la vez, lo que significa que si tiene muchos archivos en el mismo directorio (es decir, 500M de entradas de directorio) va a tomar un tiempo terriblemente largo leer todas las entradas de directorio , especialmente en un disco lento. Para los directorios que contienen una gran cantidad de archivos, deberá profundizar más que las herramientas que dependen de readdir (). Tendrá que utilizar getys () syscall directamente, en lugar de los métodos auxiliares de libc.

Podemos encontrar el código C para listar los archivos usando getdents () desde here :

Hay dos modificaciones que deberá hacer para ordenar rápidamente todos los archivos en un directorio.

Primero, aumente el tamaño del búfer de X a algo así como 5 megabytes.

#define BUF_SIZE 1024*1024*5

Luego modifique el bucle principal donde imprime la información sobre cada archivo en el directorio para omitir las entradas con inode == 0. Lo hice agregando

if (dp->d_ino != 0) printf(...);

En mi caso, realmente solo me importaban los nombres de los archivos en el directorio, así que también reescribí la declaración printf () para que solo se imprimiera el nombre del archivo.

if(d->d_ino) printf("%sn ", (char *) d->d_name);

Compilarlo (no necesita ninguna biblioteca externa, por lo que es muy simple de hacer)

gcc listdir.c -o listdir

Ahora solo corre

./listdir [directory with insane number of files]


Escribir esto aquí ya que no tengo suficientes puntos de reputación para comentar una respuesta, pero puedo dejar mi propia respuesta, lo cual no tiene sentido. De todas formas...

En cuanto a la respuesta de Christopher Schultz , sugiero cambiar stat a lstat y posiblemente agregar un limit -check para evitar el desbordamiento del búfer:

if (strlen(path) + strlen(PATH_SEPARATOR) + strlen(ent->d_name) > PATH_MAX) { fprintf(stdout, "path too long (%ld) %s%c%s", (strlen(path) + strlen(PATH_SEPARATOR) + strlen(ent->d_name)), path, PATH_SEPARATOR, ent->d_name); return; }

La sugerencia de usar lstat es evitar seguir enlaces simbólicos que podrían llevar a ciclos si un directorio contiene un enlace simbólico a un directorio principal.


Esta respuesta aquí es más rápida que casi todo lo demás en esta página para directorios muy grandes y muy anidados:

https://serverfault.com/a/691372/84703

locate -r ''.'' | grep -c "^$PWD"


La manera más rápida en Linux (la pregunta está etiquetada como Linux) es usar la llamada directa al sistema. Aquí hay un pequeño programa que cuenta los archivos (solo, sin directorios) en un directorio. Puede contar millones de archivos y es alrededor de 2,5 veces más rápido que "ls -f" y aproximadamente 1.3-1.5 veces más rápido que la respuesta de Christopher Schultz.

#define _GNU_SOURCE #include <dirent.h> #include <stdio.h> #include <fcntl.h> #include <stdlib.h> #include <sys/syscall.h> #define BUF_SIZE 4096 struct linux_dirent { long d_ino; off_t d_off; unsigned short d_reclen; char d_name[]; }; int countDir(char *dir) { int fd, nread, bpos, numFiles = 0; char d_type, buf[BUF_SIZE]; struct linux_dirent *dirEntry; fd = open(dir, O_RDONLY | O_DIRECTORY); if (fd == -1) { puts("open directory error"); exit(3); } while (1) { nread = syscall(SYS_getdents, fd, buf, BUF_SIZE); if (nread == -1) { puts("getdents error"); exit(1); } if (nread == 0) { break; } for (bpos = 0; bpos < nread;) { dirEntry = (struct linux_dirent *) (buf + bpos); d_type = *(buf + bpos + dirEntry->d_reclen - 1); if (d_type == DT_REG) { // Increase counter numFiles++; } bpos += dirEntry->d_reclen; } } close(fd); return numFiles; } int main(int argc, char **argv) { if (argc != 2) { puts("Pass directory as parameter"); return 2; } printf("Number of files in %s: %d/n", argv[1], countDir(argv[1])); return 0; }

PD: No es recursivo, pero podrías modificarlo para lograrlo.


La manera 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 sesgos de datos basados ​​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 vuelto a escribir este programa para que sea recursivo, por lo que caerá en los subdirectorios y continuará contando los 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 que sea 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 u OS / 400 o lo que sea.

Como puede ver, es mucho más complicado que el original y necesariamente: al menos una función debe existir para ser llamada recursivamente a menos que desee que el código sea 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., así que he escrito un programa que intenta ser utilizado en cualquier sistema donde compile.

Hay muy poca comprobación de errores, y la función de count sí misma no informa errores. Las únicas llamadas que realmente pueden fallar son opendir y stat (si no tienes suerte y tienes un sistema donde dirent contiene el tipo de archivo ya). No estoy paranoico sobre la comprobación de la longitud total de las rutas de subdir, pero teóricamente, el sistema no debe permitir ningún nombre de ruta que sea más largo que PATH_MAX . Si hay dudas, puedo solucionarlo, pero es solo un código más que debe ser explicado a alguien que aprende a escribir C. Este programa pretende ser un ejemplo de cómo bucear en subdirectorios recursivamente.

#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:

  1. Use 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 conteo de archivos agregado al recuento general; el nuevo comportamiento es que el directorio vinculado contará como un único archivo y su contenido no se contará.
  2. 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 un poco de suerte, esta será la última edición de esta respuesta :)

Copié 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, puedes simplemente descargar la fuente ), además de que hace que sea más fácil para cualquiera sugerir una modificación al enviar un tirón -solicitud de GitHub.

La fuente está disponible bajo Apache License 2.0. Parches * bienvenidos!

  • "parche" es lo que las personas mayores como yo llaman una "solicitud de extracción".

Me di cuenta de que no usar en el procesamiento de memoria cuando tienes una gran cantidad de datos es más rápido que "conectar" los comandos. Así que guardé el resultado en un archivo y luego lo analicé

ls -1 /path/to/dir > count.txt && cat count.txt | wc -l


Podrías probar si usar opendir() y readdir() en Perl es más rápido. Para un ejemplo de esas funciones mira here


Primeros 10 directores con la mayor cantidad de archivos.

dir = /; para i en $ (ls -1 $ {dir} | sort -n); {echo "$ (find $ {dir} $ {i} -type f | wc -l) => $ i,"; } | ordenar -nr | cabeza -10


Puede cambiar el resultado en función de sus requisitos, pero aquí hay un borrador de bash que escribí para recursivamente contar e informar el número de archivos en una serie de directorios con nombres numéricos.

dir=/tmp/count_these/ ; for i in $(ls -1 ${dir} | sort -n) ; { echo "$i => $(find ${dir}${i} -type f | wc -l),"; }

Esto busca de forma recursiva todos los archivos (no directorios) en el directorio dado y devuelve los resultados en un formato tipo hash. Los ajustes simples al comando find pueden hacer que el tipo de archivos que busca cuente sean más específicos, etc.

Resultados en algo como esto:

1 => 38, 65 => 95052, 66 => 12823, 67 => 10572, 69 => 67275, 70 => 8105, 71 => 42052, 72 => 1184,


Simplemente agregando esto por el bien de completarlo. La respuesta correcta, por supuesto, ya ha sido publicada por otra persona, pero también puede obtener un recuento de archivos y directorios con el programa en árbol.

Ejecute el tree | tail -n 1 comando tree | tail -n 1 tree | tail -n 1 para obtener la última línea, que dirá algo así como "763 directorios, 9290 archivos". Esto cuenta los archivos y las carpetas recursivamente, excluyendo los archivos ocultos, que se pueden agregar con la bandera -a . Como referencia, tardó 4,8 segundos en mi computadora, para que tree contara todo el directorio de mi casa, que era 24777 directorios, 238680 archivos. find -type f | wc -l find -type f | wc -l tomó 5.3 segundos, medio segundo más, así que creo que tree es bastante competitivo en cuanto a la velocidad.

Siempre que no tenga subcarpetas, tree es una manera rápida y fácil de contar los archivos.

Además, y solo por el gusto de hacerlo, puedes usar tree | grep ''^├'' tree | grep ''^├'' para mostrar solo los archivos / carpetas en el directorio actual - esta es básicamente una versión mucho más lenta de ls .


Sorprendentemente para mí, un hallazgo básico es muy similar a ls -f

> time ls -f my_dir | wc -l 17626 real 0m0.015s user 0m0.011s sys 0m0.009s

versus

> time find my_dir -maxdepth 1 | wc -l 17625 real 0m0.014s user 0m0.008s sys 0m0.010s

Por supuesto, los valores en el tercer decimal cambian un poco cada vez que ejecuta cualquiera de estos, por lo que son básicamente idénticos. Sin embargo, tenga en cuenta que find devuelve una unidad adicional, porque cuenta el directorio real en sí (y, como se mencionó anteriormente, ls -f devuelve dos unidades adicionales, ya que también cuenta. Y ...).


Vine aquí cuando trataba de contar los archivos en un conjunto de datos de ~ 10K carpetas con ~ 10K archivos cada uno. El problema con muchos de los enfoques es que implícitamente registran archivos de 100M, lo que lleva años.

Me tomé la libertad de extender el enfoque de christopher-schultz, por lo que admite directorios de paso a través de args (su enfoque recursivo también usa estadísticas).

Coloque lo siguiente en el archivo dircnt_args.c :

#include <stdio.h> #include <dirent.h> int main(int argc, char *argv[]) { DIR *dir; struct dirent *ent; long count; long countsum = 0; int i; for(i=1; i < argc; i++) { dir = opendir(argv[i]); count = 0; while((ent = readdir(dir))) ++count; closedir(dir); printf("%s contains %ld files/n", argv[i], count); countsum += count; } printf("sum: %ld/n", countsum); return 0; }

Después de un gcc -o dircnt_args dircnt_args.c , puede invocarlo así:

dircnt_args /your/dirs/*

En los archivos de 100M en carpetas de 10K, lo anterior completa bastante rápido (~ 5 min para la primera ejecución, seguimiento en la memoria caché: ~ 23 s).

El único otro enfoque que finalizó en menos de una hora fue ls con aproximadamente 1 minuto en el caché: ls -f /your/dirs/* | wc -l ls -f /your/dirs/* | wc -l . Sin embargo, el recuento está desactivado por un par de líneas nuevas por dir ...

Aparte de lo esperado, ninguno de mis intentos con find volvieron en una hora: - /


find, ls y perl probado contra 40 000 archivos: la misma velocidad (aunque no intenté borrar el caché):

[user@server logs]$ time find . | wc -l 42917 real 0m0.054s user 0m0.018s sys 0m0.040s [user@server logs]$ time /bin/ls -f | wc -l 42918 real 0m0.059s user 0m0.027s sys 0m0.037s

y con perl opendir / readdir, mismo tiempo:

[user@server logs]$ time perl -e ''opendir D, "."; @files = readdir D; closedir D; print scalar(@files)."/n"'' 42918 real 0m0.057s user 0m0.024s sys 0m0.033s

nota: utilicé / bin / ls -f para asegurarme de omitir la opción de alias que podría ralentizarse un poco y -f para evitar el orden de archivos. ls sin -f es dos veces más lento que find / perl, excepto si ls se usa con -f, parece ser el mismo tiempo:

[user@server logs]$ time /bin/ls . | wc -l 42916 real 0m0.109s user 0m0.070s sys 0m0.044s

También me gustaría tener algún script para preguntar directamente al sistema de archivos sin toda la información innecesaria.

pruebas basadas en la respuesta de Peter van der Heijden, Glenn Jackman y Mark4o.

Thomas


ls pasa más tiempo clasificando los nombres de los archivos, usando -f para desactivar la clasificación se guardará en algún momento:

ls -f | wc -l

o puede usar find :

find . -type f | wc -l


Por defecto ls ordena los nombres, lo que puede llevar un tiempo si hay muchos de ellos. Además, no habrá salida hasta que todos los nombres sean leídos y ordenados. Use la opción ls -f para desactivar la clasificación.

ls -f | wc -l

Tenga en cuenta que esto también habilitará -a , así que . , .. , y otros archivos que comienzan con . será contado