¿Hay un comando bash que cuente la cantidad de archivos que coinciden con un patrón?
Por ejemplo, quiero obtener el recuento de todos los archivos en un directorio que coincida con este patrón: log*
Este simple one-liner debería funcionar en cualquier shell, no solo bash:
ls -1q log* | wc -l
ls -1q le dará una línea por archivo, incluso si contienen espacios en blanco o caracteres especiales como líneas nuevas.
La salida se canaliza a wc -l, que cuenta el número de líneas.
-l, ya que eso requierestat(2)en cada archivo y con el propósito de contar no agrega nada.ls, ya que crea un proceso hijo.log*es expandido por el shell, nols, porecholo que sería simple .logsen el directorio en cuestión, también se contará el contenido de ese directorio de registros. Esto probablemente no sea intencional.Puede hacer esto de manera segura (es decir, no será molestado por archivos con espacios o
\nen su nombre) con bash:Debe habilitar
nullglobpara que no obtenga el literal*.logen la$logfilesmatriz si no coinciden los archivos. (Consulte Cómo "deshacer" un 'set -x'? Para ver ejemplos de cómo restablecerlo de manera segura).fuente
shopt -u nullglobdebe omitir sinullglobno se ha desarmado, entonces comenzó.*.logcon solo*contará directorios. Si los archivos que desea enumerar tienen la convención de nomenclatura tradicionalname.extension, use*.*.Muchas respuestas aquí, pero algunas no tienen en cuenta
-l)*.loglugar delog*logsque coincidelog*)Aquí hay una solución que los maneja a todos:
Explicación:
-Uhacelsque no se ordenen las entradas, lo que significa que no necesita cargar todo el listado de directorios en la memoria-bimprime escapes de estilo C para caracteres no gráficos, lo que hace que las nuevas líneas se impriman como\n.-aimprime todos los archivos, incluso los archivos ocultos (no es estrictamente necesario cuando el globlog*implica que no hay archivos ocultos)-dimprime directorios sin intentar enumerar el contenido del directorio, que es lo quelsnormalmente haría-1se asegura de que esté en una columna (ls hace esto automáticamente cuando escribe en una tubería, por lo que no es estrictamente necesario)2>/dev/nullredirige stderr para que si hay 0 archivos de registro, ignore el mensaje de error. (Tenga en cuenta queshopt -s nullglob, en su lugar, selsenumeraría todo el directorio de trabajo).wc -lconsume la lista de directorios a medida que se genera, por lo que la salida delsnunca está en la memoria en ningún momento.--Los nombres de archivo se separan del comando utilizando--para no ser entendidos como argumentos parals(en caso de quelog*se elimine)El shell se expandirá
log*a la lista completa de archivos, lo que puede agotar la memoria si se trata de muchos archivos, por lo que es mejor ejecutarlo a través de grep:Este último maneja directorios de archivos extremadamente grandes sin usar mucha memoria (aunque sí usa una subshell). El
-dya no es necesario, porque solo enumera el contenido del directorio actual.fuente
Para una búsqueda recursiva:
wc -ccontará el número de caracteres en la salida defind, mientras que-printf xle indicafindque imprima un soloxpara cada resultado.Para una búsqueda no recursiva, haga esto:
fuente
-name '*.log', contará todos los archivos, que es lo que necesitaba para mi caso de uso. También la bandera -maxdepth es extremadamente útil, ¡gracias!find; simplemente imprima algo más que el nombre de archivo literal.La respuesta aceptada para esta pregunta es incorrecta, pero tengo poca reputación, así que no puedo agregarle ningún comentario.
La respuesta correcta a esta pregunta la da Mat:
El problema con la respuesta aceptada es que wc -l cuenta el número de caracteres de nueva línea y los cuenta incluso si se imprimen en el terminal como '?' en la salida de 'ls -l'. Esto significa que la respuesta aceptada FALLA cuando un nombre de archivo contiene un carácter de nueva línea. He probado el comando sugerido:
e informa erróneamente un valor de 2 incluso si solo hay 1 archivo que coincide con el patrón cuyo nombre contiene un carácter de nueva línea. Por ejemplo:
fuente
Si tiene muchos archivos y no desea usar la
shopt -s nullglobsolución de matriz elegante y bash, puede usar find y así sucesivamente siempre que no imprima el nombre del archivo (que puede contener nuevas líneas).Esto encontrará todos los archivos que coincidan con log * y que no comiencen con
.*: "not name. *" Es redundante, pero es importante tener en cuenta que el valor predeterminado para "ls" es no mostrar archivos de puntos, pero el valor predeterminado para encontrar es incluirlos.Esta es una respuesta correcta y maneja cualquier tipo de nombre de archivo que pueda lanzarle, porque el nombre de archivo nunca se pasa entre los comandos.
Pero, la
shopt nullglobrespuesta es la mejor respuesta!fuente
findvs usarlsson dos formas diferentes de resolver el problema.findno siempre está presente en una máquina, perolsgeneralmente lo está,findprobablemente no tiene tampoco tiene todas esas opciones elegantesls.-maxdepth 1findhace esto por defecto. Esto puede crear confusión si uno no se da cuenta de que hay una carpeta secundaria oculta, y puede ser ventajoso usarlalsen algunas circunstancias, que no informa los archivos ocultos de manera predeterminada.Aquí está mi única línea para esto.
fuente
set --lo tanto, no está haciendo nada excepto prepararnos$#, que almacena la cantidad de argumentos de la línea de comandos que se pasaron al programa de shellPuede usar la opción -R para buscar los archivos junto con los que están dentro de los directorios recursivos
puedes usar patrones en grep
fuente
Un comentario importante
(no hay suficiente reputación para comentar)
Esto es BUGGY :
Si
shopt -s nullglobse configura, imprime el número de TODOS los archivos regulares, no solo los que tienen el patrón (probado en CentOS-8 y Cygwin). ¿Quién sabe qué otros errores sin sentidolstiene?Esto es CORRECTO y mucho más rápido:
Hace el trabajo esperado.
Y los tiempos de ejecución difieren.
El primero:
0.006en CentOS y0.083en Cygwin (en caso de que se use con cuidado).El segundo:
0.000en CentOS y0.003en Cygwin.fuente
Puede definir dicho comando fácilmente, utilizando una función de shell. Este método no requiere ningún programa externo y no genera ningún proceso secundario. No intenta el
lsanálisis peligroso y maneja caracteres "especiales" (espacios en blanco, líneas nuevas, barras invertidas, etc.) muy bien. Solo se basa en el mecanismo de expansión de nombre de archivo proporcionado por el shell. Es compatible con al menos sh, bash y zsh.La siguiente línea define una función llamada
countque imprime el número de argumentos con los que se ha llamado.Simplemente llámelo con el patrón deseado:
Para que el resultado sea correcto cuando el patrón global no tiene coincidencia, la opción de shell
nullglob(ofailglob, que es el comportamiento predeterminado en zsh) debe establecerse en el momento en que ocurre la expansión. Se puede configurar así:Dependiendo de lo que quiera contar, también podría estar interesado en la opción de shell
dotglob.Desafortunadamente, con bash al menos, no es fácil establecer estas opciones localmente. Si no desea configurarlos globalmente, la solución más sencilla es utilizar la función de esta manera más complicada:
Si desea recuperar la sintaxis ligera
count log*, o si realmente desea evitar generar una subshell, puede hackear algo en la línea de:Como beneficio adicional, esta función es de uso más general. Por ejemplo:
Al convertir la función en un archivo de script (o un programa C equivalente), invocable desde la RUTA, también se puede componer con programas como
findyxargs:fuente
He pensado mucho en esta respuesta, especialmente teniendo en cuenta las cosas de no analizar . Al principio intenté
que funcionó si solo hubiera un nombre de archivo como
pero falló si hice un nombre de archivo como este
Finalmente se me ocurrió lo que estoy poniendo a continuación. Tenga en cuenta que estaba tratando de obtener un recuento de todos los archivos en el directorio (sin incluir ningún subdirectorio). Creo que, junto con las respuestas de @Mat y @Dan_Yard, además de tener al menos la mayoría de los requisitos establecidos por @mogsie (no estoy seguro de la memoria). Creo que la respuesta de @mogsie es correcta, pero siempre trato de evitar el análisis a
lsmenos que sea una situación extremadamente específica.Más legible:
Esto está haciendo una búsqueda específica para archivos, delimitando la salida con un carácter nulo (para evitar problemas con espacios y saltos de línea), luego contando el número de caracteres nulos. El número de archivos será uno menos que el número de caracteres nulos, ya que habrá un carácter nulo al final.
Para responder la pregunta del OP, hay dos casos a considerar
1) Búsqueda no recursiva:
2) Búsqueda recursiva. Tenga en cuenta que lo que hay dentro del
-nameparámetro puede necesitar ser cambiado para un comportamiento ligeramente diferente (archivos ocultos, etc.).Si a alguien le gustaría comentar cómo se comparan estas respuestas con las que he mencionado en esta respuesta, por favor hágalo.
Tenga en cuenta que llegué a este proceso de pensamiento al obtener esta respuesta .
fuente
Esto es lo que siempre hago:
fuente
awk 'END{print NR}'debe ser equivalente awc -l.Lo que significa listar un archivo por línea y luego canalizarlo al comando de recuento de palabras con cambio de parámetro a líneas de recuento.
fuente
Para contar todo simplemente canalice ls a la línea de conteo de palabras:
Para contar con el patrón, canalice a grep primero:
fuente