¿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
, porecho
lo que sería simple .logs
en 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
\n
en su nombre) con bash:Debe habilitar
nullglob
para que no obtenga el literal*.log
en la$logfiles
matriz 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 nullglob
debe omitir sinullglob
no se ha desarmado, entonces comenzó.*.log
con 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
)*.log
lugar delog*
logs
que coincidelog*
)Aquí hay una solución que los maneja a todos:
Explicación:
-U
hacels
que no se ordenen las entradas, lo que significa que no necesita cargar todo el listado de directorios en la memoria-b
imprime escapes de estilo C para caracteres no gráficos, lo que hace que las nuevas líneas se impriman como\n
.-a
imprime todos los archivos, incluso los archivos ocultos (no es estrictamente necesario cuando el globlog*
implica que no hay archivos ocultos)-d
imprime directorios sin intentar enumerar el contenido del directorio, que es lo quels
normalmente haría-1
se 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/null
redirige stderr para que si hay 0 archivos de registro, ignore el mensaje de error. (Tenga en cuenta queshopt -s nullglob
, en su lugar, sels
enumeraría todo el directorio de trabajo).wc -l
consume la lista de directorios a medida que se genera, por lo que la salida dels
nunca 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
-d
ya no es necesario, porque solo enumera el contenido del directorio actual.fuente
Para una búsqueda recursiva:
wc -c
contará el número de caracteres en la salida defind
, mientras que-printf x
le indicafind
que imprima un solox
para 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 nullglob
solució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 nullglob
respuesta es la mejor respuesta!fuente
find
vs usarls
son dos formas diferentes de resolver el problema.find
no siempre está presente en una máquina, perols
generalmente lo está,find
probablemente no tiene tampoco tiene todas esas opciones elegantesls
.-maxdepth 1
find
hace 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 usarlals
en 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 nullglob
se 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 sentidols
tiene?Esto es CORRECTO y mucho más rápido:
Hace el trabajo esperado.
Y los tiempos de ejecución difieren.
El primero:
0.006
en CentOS y0.083
en Cygwin (en caso de que se use con cuidado).El segundo:
0.000
en CentOS y0.003
en 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
ls
aná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
count
que 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
find
yxargs
: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
ls
menos 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
-name
pará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