CentOS 5.9
Me encontré con un problema el otro día en el que un directorio tenía muchos archivos. Para contarlo, corríls -l /foo/foo2/ | wc -l
Resulta que había más de 1 millón de archivos en un solo directorio (larga historia, la causa raíz se está solucionando).
Mi pregunta es: ¿hay una forma más rápida de hacer el recuento? ¿Cuál sería la forma más eficiente de obtener el recuento?

ls -l|wc -lestaría apagado por uno debido a los bloques totales en la primera línea dels -lsalida-Abandera.-lTambién es problemático debido a la lectura de metadatos del archivo para generar el formato de lista extendida. Forzar NO-lmediante el uso\lses una opción mucho mejor (-1se asume cuando se canaliza la salida). Consulte la respuesta de Gilles para obtener la mejor solución aquí.ls -lno genera ningún archivo oculto ni las entradas.y...ls -ala salida incluye archivos ocultos, incluidos.y..mientras que lals -Asalida incluye archivos ocultos excluyendo.y... En la respuesta de Gilles, ladotglobopción bash shell hace que la expansión incluya archivos ocultos excluyendo.y...Respuestas:
Respuesta corta:
(Esto incluye
.y.., por lo tanto, reste 2.)Cuando enumera los archivos en un directorio, pueden suceder tres cosas comunes:
lscomando hacen eso.statpara recuperar metadatos sobre cada entrada de directorio, como si es un directorio.El # 3 es el más caro con diferencia, ya que requiere cargar un inodo para cada archivo. En comparación, todos los nombres de archivo necesarios para el n. ° 1 se almacenan de forma compacta en unos pocos bloques. # 2 desperdicia algo de tiempo de CPU pero a menudo no es un factor decisivo.
Si no hay líneas nuevas en los nombres de archivo, un simple
ls -A | wc -lle indica cuántos archivos hay en el directorio. Tenga en cuenta que si tiene un alias parals, esto puede desencadenar una llamada astat(por ejemplo,ls --colorols -Fnecesita saber el tipo de archivo, que requiere una llamada astat), así que desde la línea de comando, llamecommand ls -A | wc -lo\ls -A | wc -lpara evitar un alias.Si hay nuevas líneas en el nombre del archivo, si las nuevas líneas se enumeran o no depende de la variante de Unix. GNU coreutils y BusyBox se muestran
?de forma predeterminada para una nueva línea, por lo que son seguros.Llame
ls -fpara enumerar las entradas sin ordenarlas (# 2). Esto se activa automáticamente-a(al menos en los sistemas modernos). La-fopción está en POSIX pero con estado opcional; la mayoría de las implementaciones lo admiten, pero no BusyBox. La opción-qreemplaza los caracteres no imprimibles, incluidas las nuevas líneas por?; es POSIX pero no es compatible con BusyBox, así que omítalo si necesita soporte de BusyBox a expensas de contar en exceso los archivos cuyo nombre contiene un carácter de nueva línea.Si el directorio no tiene subdirectorios, entonces la mayoría de las versiones de
findno invocaránstatsus entradas (optimización del directorio hoja: un directorio que tiene un recuento de enlaces de 2 no puede tener subdirectorios, por lofindque no necesita buscar los metadatos de las entradas a menos que condición como lo-typerequiere). Entonces,find . | wc -les una forma portátil y rápida de contar archivos en un directorio, siempre que el directorio no tenga subdirectorios y que ningún nombre de archivo contenga una nueva línea.Si el directorio no tiene subdirectorios pero los nombres de los archivos pueden contener líneas nuevas, pruebe con uno de estos (el segundo debería ser más rápido si es compatible, pero puede que no sea notablemente).
Por otro lado, no use
findsi el directorio tiene subdirectorios: inclusofind . -maxdepth 1llamadasstaten cada entrada (al menos con GNU find y BusyBox find). Evita la clasificación (# 2) pero paga el precio de una búsqueda de inodo (# 3) que mata el rendimiento.En el shell sin herramientas externas, puede ejecutar contar los archivos en el directorio actual con
set -- *; echo $#. Esto pierde archivos de puntos (archivos cuyo nombre comienza con.) e informa 1 en lugar de 0 en un directorio vacío. Esta es la forma más rápida de contar archivos en directorios pequeños porque no requiere iniciar un programa externo, pero (excepto en zsh) pierde tiempo para directorios más grandes debido al paso de clasificación (# 2).En bash, esta es una forma confiable de contar los archivos en el directorio actual:
En ksh93, esta es una forma confiable de contar los archivos en el directorio actual:
En zsh, esta es una forma confiable de contar los archivos en el directorio actual:
Si usted tiene el
mark_dirsconjunto de opciones, asegúrese de apagarlo:a=(*(DNoN^M)).En cualquier shell POSIX, esta es una forma confiable de contar los archivos en el directorio actual:
Todos estos métodos ordenan los nombres de los archivos, excepto el zsh.
fuente
find -maxdepth 1se mantiene fácilmente siempre\ls -Uque no agregue nada como una-typedeclaración que tiene que hacer más comprobaciones. ¿Estás seguro de que GNU encuentra realmente llamadasstat? Incluso la desaceleraciónfind -typeno es nada en comparación con la cantidad dels -lpantanos si hace que devuelva los detalles del archivo. Por otro lado, el ganador de velocidad clara estázshusando el globo no clasificador. (los globos clasificados son 2 veces más lentos quelsmientras que los no clasificados son 2 veces más rápidos). Me pregunto si los tipos de sistemas de archivos afectarían significativamente estos resultados.strace. Esto solo es cierto si el directorio tiene subdirectorios: de lo contrariofind, la optimización del directorio hoja se activa (incluso sin-maxdepth 1), debería haber mencionado eso. Muchas cosas pueden afectar el resultado, incluido el tipo de sistema de archivos (la llamadastates mucho más costosa en sistemas de archivos que representan directorios como listas lineales que en sistemas de archivos que representan directorios como árboles), si los inodos se crearon todos juntos y, por lo tanto, están cerca en el disco, caché frío o caliente, etc.ls -fha sido la forma confiable de evitar llamadasstat: esto a menudo se describe simplemente hoy como "la salida no está ordenada" (que también causa), e incluye.y...-Ay-Uno son opciones estándar.\ls -afq *[0-9].pdb | wc -lversion sh (AT&T Research) 93u+ 2012-08-01en mi sistema basado en Debian,FIGNOREno parece funcionar. Las entradas.y..se incluyen en la matriz resultanteEs considerablemente más rápido en mi máquina, pero el
.directorio local se agrega al recuento.fuente
-typeparámetrofinddebería ser más rápido quels-mindepth 1para omitir el directorio en sí.ls -1Uantes de que la tubería gaste un poco menos de recursos, ya que no intenta ordenar las entradas del archivo, solo las lee a medida que se ordenan en la carpeta del disco. También produce menos salida, lo que significa un poco menos de trabajowc.También podría usar
ls -fcuál es más o menos un atajo parals -1aU.Sin embargo, no sé si hay una forma eficiente de recursos para hacerlo a través de un comando sin tuberías.
fuente
Otro punto de comparación. Si bien no es un shell oneliner, este programa C no hace nada superfluo. Tenga en cuenta que los archivos ocultos se ignoran para que coincidan con la salida de
ls|wc -l(ls -l|wc -lestá desactivado en uno debido a los bloques totales en la primera línea de salida).fuente
readdir()API stdio agrega cierta sobrecarga y no le da control sobre el tamaño del búfer pasado a la llamada del sistema subyacente (getdentsen Linux)Tu podrías intentar
perl -e 'opendir($dh,".");$i=0;while(readdir $dh){$i++};print "$i\n";'Sería interesante comparar los tiempos con su tubería de shell.
fuente
find -maxdepth 1 | wc -l,\ls -AU | wc -ly elzshpegote no clasificación y recuento de matriz basada). En otras palabras, supera las opciones con varias ineficiencias, como ordenar o leer propiedades de archivos extraños. Me atrevería a decir que ya que tampoco te da nada, no vale la pena usar una solución más simple a menos que ya estés en Perl :).y..en el recuento, por lo que debe restar dos para obtener el número real de archivos (y subdirectorios). En Perl moderno,perl -E 'opendir $dh, "."; $i++ while readdir $dh; say $i - 2'lo haría.A partir de esta respuesta , puedo pensar en esta como una posible solución.
Copie el programa C anterior en el directorio en el que deben enumerarse los archivos. Luego ejecute estos comandos:
fuente
ls -f, no filtred_typeen absoluto, solo end->d_ino != 0; 3) restar 2 para.y...ls -f.Una solución solo para bash, que no requiere ningún programa externo, pero no sabe cuánto eficiente:
fuente
Probablemente, la forma más eficiente de recursos no implicaría invocaciones de procesos externos. Entonces apostaría por ...
fuente
Después de solucionar el problema de la respuesta de @Joel, donde se agregó
.como un archivo:find /foo/foo2 -maxdepth 1 | tail -n +2 | wc -ltailsimplemente elimina la primera línea, lo que significa que.ya no se cuenta.fuente
wcentrada no es muy eficiente ya que la sobrecarga aumenta linealmente con respecto al tamaño de entrada. En este caso, ¿por qué no simplemente disminuir el recuento final a compensar por ello estar fuera por uno, que es una operación de tiempo constante:echo $(( $(find /foo/foo2 -maxdepth 1 | wc -l) - 1))let count = $(find /foo/foo2 -maxdepth 1 | wc -l) - 2os.listdir () en python puede hacer el trabajo por usted. Proporciona una matriz de los contenidos del directorio, excluyendo el especial '.' y '...' archivos. Además, no es necesario preocuparse por los archivos abt con caracteres especiales como '\ n' en el nombre.
El siguiente es el tiempo que tarda el comando anterior de python en comparación con el comando 'ls -Af'.
~ / test $ time ls -Af | wc -l 399144 0m0.300 reales usuario 0m0.104s sys 0m0.240s ~ / test $ time python -c 'import os; print len (os.listdir ("."))' 399142 0m0.249s reales usuario 0m0.064s sys 0m0.180sfuente
ls -1 | wc -lviene inmediatamente a mi mente Sils -1Ues más rápido quels -1puramente académico, la diferencia debería ser insignificante, pero para directorios muy grandes.fuente
Para excluir subdirectorios del conteo, aquí hay una variación en la respuesta aceptada de Gilles:
La
$(( ))expansión aritmética externa resta la salida del segundo$( )subshell del primero$( ). El primero$( )es exactamente el de Gilles desde arriba. El segundo$( )genera el recuento de directorios "vinculados" al destino. Esto viene dels -od(sustitúyalols -ldsi lo desea), donde la columna que enumera el recuento de enlaces duros tiene un significado especial para los directorios. El recuento de "enlace" incluye.,..y todos los subdirectorios.No probé el rendimiento, pero parece ser similar. Agrega una estadística del directorio de destino y algo de sobrecarga para la subshell y la tubería agregadas.
fuente
Creo que echo * sería más eficiente que cualquier comando 'ls':
fuente
echo 'Hello World'|wc -wproduce2.