¿Hay algo más rápido que `find. El | wc -l` para contar archivos en un directorio?

8

No es raro que tenga que contar la cantidad de archivos en un directorio, a veces esto llega a millones.

¿Hay una mejor manera que simplemente enumerarlos y contarlos find . | wc -l? ¿Hay algún tipo de llamada al sistema de archivos que pueda hacer en ext3 / 4 que requiera menos E / S?

MattPark
fuente
3
Estás contando no solo los archivos, sino también los directorios. Si solo desea contar archivos, use "find. -Type f | wc -l" si desea contar enlaces simbólicos y archivos regulares, use "find. -Type f -or -type l | wc -l"
FSMaxB
Un directorio es un tipo de archivo, al igual que dispositivos, enlaces simbólicos y sockets. Los archivos regulares son un subconjunto de archivos.
Toby Speight
1
El ejemplo que da sugiere que desea un recuento recursivo ; si no, entonces lo necesita find -maxdepth 1. Tenga en cuenta que con su enfoque actual, contará dos veces cualquier nombre que contenga un carácter de nueva línea.
Toby Speight

Respuestas:

13

No es una aceleración fundamental, pero al menos algo :)

find . -printf \\n | wc -l

Realmente no necesita pasar la lista de nombres de archivos, solo bastan las nuevas líneas. Esta variante es aproximadamente un 15% más rápida en mi Ubuntu 12.04.3 cuando los directorios se almacenan en caché en la RAM. Además, esta variante funcionará correctamente con nombres de archivo que contengan nuevas líneas.

Curiosamente, esta variante parece ser un poco más lenta que la anterior:

find . -printf x | wc -c

Caso especial, pero muy rápido

Si el directorio está en su propio sistema de archivos, simplemente puede contar los inodos:

df -i .

Si el número de directorios y archivos en directorios diferentes al contado no cambia mucho, simplemente puede restar este número conocido del df -iresultado actual . De esta manera podrá contar los archivos y directorios muy rápidamente.

pabouk
fuente
"Esta variante es aproximadamente un 15% más rápida ..." me hace preguntarme si hay algún tipo de truco útil que estés usando para cronometrar esto.
Brian Z
44
@BrianZ: Puede cronometrar un comando anteponiendo el comando con tiempo. time find /usr/src/ -printf \\n | wc -l, puede borrar los cachés entre ejecuciones consudo sync && sudo sysctl -w vm.drop_caches=3
MattPark
Así que vi un aumento constante del 2% en la velocidad con cualquiera de las 2 primeras opciones sin almacenamiento en caché. Así que sí, esa es una forma genial de hacerlo. Contar los inodes es definitivamente lo mejor si su entorno está configurado para eso. No lo había considerado.
MattPark
Se -printf xsupone que es lo mismo que -printf '\0'? No lo veo mencionado en los documentos.
CMCDragonkai
@CMCDragonkai: la acción -printffunciona de manera similar a la printf()función en C con la principal diferencia de que las %directivas tienen un significado diferente. La acción se invoca para cada archivo encontrado. Esto significa que -printf ximprimirá el carácter xpara cada archivo encontrado (¡pruébelo!) -printf '\0'E imprimirá el carácter NULL (código ASCII 0) para cada archivo encontrado. -printf '\0'No tiene un significado especial. Ambos funcionarán igual en el ejemplo con wc -cen esta respuesta.
pabouk
3

He escrito ffcnt exactamente para ese propósito. Recupera el desplazamiento físico de los directorios con el fiemapioctl y luego programa el recorrido del directorio en múltiples pases secuenciales para reducir el acceso aleatorio. Si realmente obtienes una aceleración en comparación con find | wc depende de varios factores:

  • tipo de sistema de archivos: los sistemas de archivos como ext4 que admiten fiemapioctl se beneficiarán más
  • velocidad de acceso aleatorio: los discos duros se benefician mucho más que los SSD
  • diseño de directorio: cuanto mayor sea el número de directorios anidados, mayor será el potencial de optimización

(re) montar con relatimeo incluso nodiratimepuede mejorar la velocidad (para todos los métodos) cuando los accesos causarían actualizaciones de metadatos.

the8472
fuente
¡Esa última oración es un consejo que vale la pena! Creo que el enlace a su programa mejoraría si agrega un resumen de cómo funciona. Preferimos respuestas que estén completas en sí mismas, en caso de que algo malo le suceda al recurso vinculado (pero mantenga el enlace también, por supuesto).
Toby Speight
2

En realidad, en mi sistema (Arch Linux) este comando

   ls -A | wc -l

es más rápido que todo lo anterior:

   $ time find . | wc -l
  1893

   real    0m0.027s
   user    0m0.004s
   sys     0m0.004s
   $ time find . -printf \\n  | wc -l
   1893

   real    0m0.009s
   user    0m0.000s
   sys     0m0.008s
   $ time find . -printf x  | wc -c
   1893

   real    0m0.009s
   user    0m0.000s
   sys     0m0.008s
   $ time ls -A | wc -l
   1892

   real    0m0.007s
   user    0m0.000s
   sys     0m0.004s
MariusMatutiae
fuente
Sin embargo, creo que el problema con ls es que a menudo devuelve algo como /bin/ls: Argument list too longsi usa globbing, pero nuevamente puede funcionar de forma recursiva como find también, así que tal vez sea algo a considerar, no use find si no es necesario.
MattPark
Parece muy tarde (muchos años) comentar al respecto, pero ls -Aenumere solo los archivos en el directorio actual, mientras que findsin -maxdepth 1argumento hará una búsqueda recursiva en todos los subdirectorios.
Luciano