¿Qué se expande a todos los archivos en el directorio actual de forma recursiva?
92
Sé que se **/*.extexpande a todos los archivos en todos los subdirectorios que coinciden *.ext, pero ¿cuál es una expansión similar que incluye todos esos archivos en el directorio actual también?
Mi fiesta no funciona **/*.ext. ¿Estás seguro de que te funciona?
Tangens
@tangens Tienes que habilitar la globstaropción según la respuesta de Dennis.
kenorb
Respuestas:
111
Esto funcionará en Bash 4:
ls -l {,**/}*.ext
Para que funcione el globo de doble asterisco, globstares necesario configurar la opción (predeterminada: activada):
shopt -s globstar
De man bash:
globstar
Si se establece, el patrón ** utilizado en una expansión de nombre de archivo
el texto coincidirá con archivos y cero o más directorios y
subdirectorios. Si el patrón va seguido de una /, solo
los directorios y subdirectorios coinciden.
Ahora me pregunto si alguna vez pudo haber habido un error en el procesamiento de globstar, porque ahora usando simplemente ls **/*.extestoy obteniendo resultados correctos.
Independientemente, miré el análisis que hizo kenorb usando el repositorio de VLC y encontré algunos problemas con ese análisis y en mi respuesta inmediatamente anterior:
Las comparaciones con la salida del findcomando no son válidas ya que la especificación -type fno incluye otros tipos de archivos (directorios en particular) y es lsprobable que los comandos enumerados sí lo hagan. Además, uno de los comandos enumerados, ls -1 {,**/}*.*que parece estar basado en el mío anterior, solo genera nombres que incluyen un punto para los archivos que están en subdirectorios. La pregunta del OP y mi respuesta incluyen un punto, ya que lo que se busca son archivos con una extensión específica.
Sin embargo, lo más importante es que existe un problema especial al usar el lscomando con el patrón globstar **. Surgen muchos duplicados ya que Bash expande el patrón a todos los nombres de archivo (y nombres de directorio) en el árbol que se está examinando. Posteriormente a la expansión, el lscomando enumera cada uno de ellos y su contenido si son directorios.
Ejemplo:
En nuestro directorio actual está el subdirectorio Ay su contenido:
A
└── AB
└── ABC
├── ABC1
├── ABC2
└── ABCD
└── ABCD1
En ese árbol, se **expande a "AA / AB A / AB / ABC A / AB / ABC / ABC1 A / AB / ABC / ABC2 A / AB / ABC / ABCD A / AB / ABC / ABCD / ABCD1" (7 entradas) . Si lo hace, echo **ese es el resultado exacto que obtendrá y cada entrada se representará una vez. Sin embargo , si lo hace ls **, generará una lista de cada una de esas entradas. Entonces, esencialmente, lo hace ls Aseguido de ls A/AB, etc., por lo que A/ABse muestra dos veces. Además, lsva a diferenciar la salida de cada subdirectorio:
Por lo tanto, usar wc -lcuenta todas esas líneas en blanco y encabezados de sección de nombre de directorio, lo que hace que el conteo se desvanezca aún más.
Esta es otra razón más por la que no debe analizarls .
Como resultado de este análisis adicional, recomiendo no usar el patrón globstar en ninguna circunstancia que no sea iterar sobre un árbol de archivos de esta manera:
for entry in **
do
something "$entry"done
Como comparación final, utilicé un repositorio de fuentes Bash que tenía a mano e hice esto:
Solía trcambiar los espacios a nuevas líneas, lo cual solo es válido aquí ya que ningún nombre incluye espacios. Solía sedeliminar el interlineado ./de cada línea de salida de find. Ordené la salida de findya que normalmente no está ordenada y la expansión de globos de Bash ya está ordenada. Como puede ver, la única salida de difffue la .salida del directorio actual por find. Cuando lo hice, ls ** | wc -lla salida tenía casi el doble de líneas.
Probé Ubuntu y Cygwin, y globstarestá predeterminadooff
Steven Penny
12
¡La mejor respuesta! pero creo que **/*.extdebería ser suficiente. Además, no tendrá los archivos ocultos a menos que usted shopt -s dotglob.
gniourf_gniourf
2
Para desactivar globstar: shopt -u globstar.
Kenorb
4
@gniourf_gniourf La pregunta en realidad pide incluir el directorio actual específicamente para que no, **/*.extno sea suficiente
msciwoj
2
@dotnetCarpenter: La versión de Bash que se envía con MacOS es 3.2, que no es compatible con globstar, como descubrió. Un asterisco doble se trata igual que uno solo. Globstar se introdujo en Bash 4.0.
Pausado hasta nuevo aviso.
13
Esto imprimirá todos los archivos en el directorio actual y sus subdirectorios que terminan en '.ext'.
Si bien esta respuesta no cumple con la "expansión" solicitada por el OP en el sentido más estricto, es más probable que produzca el resultado deseado.
Pausado hasta nuevo aviso.
7
Puede usar: **/*.*para incluir todos los archivos de forma recursiva (habilitar por:) shopt -s globstar.
A continuación, encontrará pruebas de otras variaciones y cómo se comportan.
Carpeta de prueba con 3472 archivos en la carpeta del repositorio de muestra de VLC :
(Files total de 3472 contados según: find . -type f | wc -l)
ls -1 **/*.* - devuelve 3338
ls -1 {,**/}*.*- devuelve 3341 (según lo propuesto por Dennis )
ls -1 {,**/}* - devuelve 8265
ls -1 **/*- devuelve 7817, excepto archivos ocultos (según lo propuesto por Dennis )
ls -1 **/{.[^.],}*- devuelve 7869 (según lo propuesto por Dennis )
ls -1 {,**/}.?* - devuelve 15855
ls -1 {,**/}.* - devuelve 20321
Así que creo que el método más cercano para enumerar todos los archivos de forma recursiva es el primer ejemplo ( **/*.*) según el comentario de gniourf-gniourf (asumiendo que los archivos tienen las extensiones adecuadas, o usan la específica), ya que el segundo ejemplo da algunos duplicados más como a continuación :
Para incluir archivos ocultos, use: shopt -s dotglob(deshabilitar por shopt -u dotglob). No se recomienda, porque puede afectar a comandos como mvo rmy puede eliminar accidentalmente los archivos incorrectos.
En la terminal Mac y bash con globstar habilitado, encontré la solución anterior ( **/*.*) informativa y funcionó mejor. La respuesta aceptada provocó duplicados de elementos en el directorio superior. Mi patrón de trabajo fue:"${path}"**/*.*
mummybot
Sería interesante probar esto con otras opciones como nullglob y dotglob
Wilf
4
$ find . -type f
Eso listará todos los archivos en el directorio actual. Luego puede hacer algún otro comando en la salida usando -exec
$find . -type f -exec grep "foo" {} \;
Eso grep cada archivo de la búsqueda de la cadena "foo".
Ahora que han pasado 11 años, podría ser el momento de que alguien señale que se find . -type faplica de forma recursiva con la raíz en el directorio actual, no solo en el directorio actual.
Roger Dahl
4
¿Por qué no usar la expansión de llaves para incluir también el directorio actual?
./{*,**/*}.ext
La expansión de llaves ocurre antes de la expansión glob, por lo que puede hacer de manera efectiva lo que quiera con versiones anteriores de bash y puede renunciar a jugar con globstar en versiones más nuevas.
Además, se considera una buena práctica en bash incluir el líder ./en sus patrones globales.
**/*.ext
. ¿Estás seguro de que te funciona?globstar
opción según la respuesta de Dennis.Respuestas:
Esto funcionará en Bash 4:
Para que funcione el globo de doble asterisco,
globstar
es necesario configurar la opción (predeterminada: activada):shopt -s globstar
De
man bash
:Ahora me pregunto si alguna vez pudo haber habido un error en el procesamiento de globstar, porque ahora usando simplemente
ls **/*.ext
estoy obteniendo resultados correctos.Independientemente, miré el análisis que hizo kenorb usando el repositorio de VLC y encontré algunos problemas con ese análisis y en mi respuesta inmediatamente anterior:
Las comparaciones con la salida del
find
comando no son válidas ya que la especificación-type f
no incluye otros tipos de archivos (directorios en particular) y esls
probable que los comandos enumerados sí lo hagan. Además, uno de los comandos enumerados,ls -1 {,**/}*.*
que parece estar basado en el mío anterior, solo genera nombres que incluyen un punto para los archivos que están en subdirectorios. La pregunta del OP y mi respuesta incluyen un punto, ya que lo que se busca son archivos con una extensión específica.Sin embargo, lo más importante es que existe un problema especial al usar el
ls
comando con el patrón globstar**
. Surgen muchos duplicados ya que Bash expande el patrón a todos los nombres de archivo (y nombres de directorio) en el árbol que se está examinando. Posteriormente a la expansión, ells
comando enumera cada uno de ellos y su contenido si son directorios.Ejemplo:
En nuestro directorio actual está el subdirectorio
A
y su contenido:En ese árbol, se
**
expande a "AA / AB A / AB / ABC A / AB / ABC / ABC1 A / AB / ABC / ABC2 A / AB / ABC / ABCD A / AB / ABC / ABCD / ABCD1" (7 entradas) . Si lo hace,echo **
ese es el resultado exacto que obtendrá y cada entrada se representará una vez. Sin embargo , si lo hacels **
, generará una lista de cada una de esas entradas. Entonces, esencialmente, lo hacels A
seguido dels A/AB
, etc., por lo queA/AB
se muestra dos veces. Además,ls
va a diferenciar la salida de cada subdirectorio:Por lo tanto, usar
wc -l
cuenta todas esas líneas en blanco y encabezados de sección de nombre de directorio, lo que hace que el conteo se desvanezca aún más.Esta es otra razón más por la que no debe analizar
ls
.Como resultado de este análisis adicional, recomiendo no usar el patrón globstar en ninguna circunstancia que no sea iterar sobre un árbol de archivos de esta manera:
for entry in ** do something "$entry" done
Como comparación final, utilicé un repositorio de fuentes Bash que tenía a mano e hice esto:
shopt -s globstar dotglob diff <(echo ** | tr ' ' '\n') <(find . | sed 's|\./||' | sort) 0a1 > .
Solía
tr
cambiar los espacios a nuevas líneas, lo cual solo es válido aquí ya que ningún nombre incluye espacios. Solíased
eliminar el interlineado./
de cada línea de salida defind
. Ordené la salida defind
ya que normalmente no está ordenada y la expansión de globos de Bash ya está ordenada. Como puede ver, la única salida dediff
fue la.
salida del directorio actual porfind
. Cuando lo hice,ls ** | wc -l
la salida tenía casi el doble de líneas.fuente
globstar
está predeterminadooff
**/*.ext
debería ser suficiente. Además, no tendrá los archivos ocultos a menos que ustedshopt -s dotglob
.globstar
:shopt -u globstar
.**/*.ext
no sea suficienteEsto imprimirá todos los archivos en el directorio actual y sus subdirectorios que terminan en '.ext'.
find . -name '*.ext' -print
fuente
Puede usar:
**/*.*
para incluir todos los archivos de forma recursiva (habilitar por:)shopt -s globstar
.A continuación, encontrará pruebas de otras variaciones y cómo se comportan.
Carpeta de prueba con 3472 archivos en la carpeta del repositorio de muestra de VLC :
(Files total de 3472 contados según:
find . -type f | wc -l
)ls -1 **/*.*
- devuelve 3338ls -1 {,**/}*.*
- devuelve 3341 (según lo propuesto por Dennis )ls -1 {,**/}*
- devuelve 8265ls -1 **/*
- devuelve 7817, excepto archivos ocultos (según lo propuesto por Dennis )ls -1 **/{.[^.],}*
- devuelve 7869 (según lo propuesto por Dennis )ls -1 {,**/}.?*
- devuelve 15855ls -1 {,**/}.*
- devuelve 20321Así que creo que el método más cercano para enumerar todos los archivos de forma recursiva es el primer ejemplo (
**/*.*
) según el comentario de gniourf-gniourf (asumiendo que los archivos tienen las extensiones adecuadas, o usan la específica), ya que el segundo ejemplo da algunos duplicados más como a continuación :y el otro genera aún más duplicados.
Para incluir archivos ocultos, use:
shopt -s dotglob
(deshabilitar porshopt -u dotglob
). No se recomienda, porque puede afectar a comandos comomv
orm
y puede eliminar accidentalmente los archivos incorrectos.fuente
**/*.*
) informativa y funcionó mejor. La respuesta aceptada provocó duplicados de elementos en el directorio superior. Mi patrón de trabajo fue:"${path}"**/*.*
$ find . -type f
Eso listará todos los archivos en el directorio actual. Luego puede hacer algún otro comando en la salida usando -exec
$find . -type f -exec grep "foo" {} \;
Eso grep cada archivo de la búsqueda de la cadena "foo".
fuente
find . -type f
aplica de forma recursiva con la raíz en el directorio actual, no solo en el directorio actual.¿Por qué no usar la expansión de llaves para incluir también el directorio actual?
La expansión de llaves ocurre antes de la expansión glob, por lo que puede hacer de manera efectiva lo que quiera con versiones anteriores de bash y puede renunciar a jugar con globstar en versiones más nuevas.
Además, se considera una buena práctica en bash incluir el líder
./
en sus patrones globales.fuente