Sé que se **/*.ext
expande 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?
Respuestas:
Esto funcionará en Bash 4:
ls -l {,**/}*.ext
Para que funcione el globo de doble asterisco, globstar
es 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 **/*.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 es ls
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, el ls
comando enumera cada uno de ellos y su contenido si son directorios.
Ejemplo:
En nuestro directorio actual está el subdirectorio A
y 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 A
seguido de ls A/AB
, etc., por lo que A/AB
se muestra dos veces. Además, ls
va a diferenciar la salida de cada subdirectorio:
...
<blank line>
directory name:
content-item
content-item
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 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 tr
cambiar los espacios a nuevas líneas, lo cual solo es válido aquí ya que ningún nombre incluye espacios. Solía sed
eliminar el interlineado ./
de cada línea de salida de find
. Ordené la salida de find
ya que normalmente no está ordenada y la expansión de globos de Bash ya está ordenada. Como puede ver, la única salida de diff
fue la .
salida del directorio actual por find
. Cuando lo hice, ls ** | wc -l
la salida tenía casi el doble de líneas.
globstar
está predeterminadooff
**/*.ext
debería ser suficiente. Además, no tendrá los archivos ocultos a menos que usted shopt -s dotglob
.
globstar
: shopt -u globstar
.
**/*.ext
no sea suficiente
Esto imprimirá todos los archivos en el directorio actual y sus subdirectorios que terminan en '.ext'.
find . -name '*.ext' -print
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 :
$ 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 mv
o rm
y puede eliminar accidentalmente los archivos incorrectos.
**/*.*
) 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".
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?
./{*,**/*}.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?