¿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?

Ramon
fuente
4
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:

...
<blank line>
directory name:
content-item
content-item

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:

shopt -s globstar dotglob
diff <(echo ** | tr ' ' '\n') <(find . | sed 's|\./||' | sort)
0a1
> .

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.

Pausado hasta nuevo aviso.
fuente
5
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'.

find . -name '*.ext' -print
unutbu
fuente
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 :

$ diff -u <(ls -1 {,**/}*.*) <(ls -1 **/*.*)
--- /dev/fd/63  2015-04-19 15:25:07.000000000 +0100
+++ /dev/fd/62  2015-04-19 15:25:07.000000000 +0100
@@ -1,6 +1,4 @@
 COPYING.LIB
-COPYING.LIB
-Makefile.am
 Makefile.am
@@ -45,7 +43,6 @@
 compat/tdestroy.c
 compat/vasprintf.c
 configure.ac
-configure.ac

y el otro genera aún más duplicados.


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.

Kenorb
fuente
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".

Amir Afghani
fuente
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.

clon206
fuente