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 -l
estaría apagado por uno debido a los bloques totales en la primera línea dels -l
salida-A
bandera.-l
También es problemático debido a la lectura de metadatos del archivo para generar el formato de lista extendida. Forzar NO-l
mediante el uso\ls
es una opción mucho mejor (-1
se asume cuando se canaliza la salida). Consulte la respuesta de Gilles para obtener la mejor solución aquí.ls -l
no genera ningún archivo oculto ni las entradas.
y..
.ls -a
la salida incluye archivos ocultos, incluidos.
y..
mientras que lals -A
salida incluye archivos ocultos excluyendo.
y..
. En la respuesta de Gilles, ladotglob
opció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:
ls
comando hacen eso.stat
para 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 -l
le 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 --color
ols -F
necesita saber el tipo de archivo, que requiere una llamada astat
), así que desde la línea de comando, llamecommand ls -A | wc -l
o\ls -A | wc -l
para 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 -f
para enumerar las entradas sin ordenarlas (# 2). Esto se activa automáticamente-a
(al menos en los sistemas modernos). La-f
opción está en POSIX pero con estado opcional; la mayoría de las implementaciones lo admiten, pero no BusyBox. La opción-q
reemplaza 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
find
no invocaránstat
sus entradas (optimización del directorio hoja: un directorio que tiene un recuento de enlaces de 2 no puede tener subdirectorios, por lofind
que no necesita buscar los metadatos de las entradas a menos que condición como lo-type
requiere). Entonces,find . | wc -l
es 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
find
si el directorio tiene subdirectorios: inclusofind . -maxdepth 1
llamadasstat
en 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_dirs
conjunto 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 1
se mantiene fácilmente siempre\ls -U
que no agregue nada como una-type
declaración que tiene que hacer más comprobaciones. ¿Estás seguro de que GNU encuentra realmente llamadasstat
? Incluso la desaceleraciónfind -type
no es nada en comparación con la cantidad dels -l
pantanos si hace que devuelva los detalles del archivo. Por otro lado, el ganador de velocidad clara estázsh
usando el globo no clasificador. (los globos clasificados son 2 veces más lentos quels
mientras 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 llamadastat
es 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 -f
ha 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..
.-A
y-U
no son opciones estándar.\ls -afq *[0-9].pdb | wc -l
version sh (AT&T Research) 93u+ 2012-08-01
en mi sistema basado en Debian,FIGNORE
no 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
-type
parámetrofind
debería ser más rápido quels
-mindepth 1
para omitir el directorio en sí.ls -1U
antes 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 -f
cuá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 -l
está 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 (getdents
en 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 -l
y elzsh
pegote 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_type
en 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 -l
tail
simplemente elimina la primera línea, lo que significa que.
ya no se cuenta.fuente
wc
entrada 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) - 2
os.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'.
fuente
ls -1 | wc -l
viene inmediatamente a mi mente Sils -1U
es más rápido quels -1
puramente 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 -ld
si 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 -w
produce2
.