Estoy intentando expandir una cadena que incluye un comodín y una colección de extensiones especificadas entre llaves. Nada parece funcionar como lo ilustra el siguiente ejemplo. la variable firstList
se expande bien, pero ninguno de los dos secondList
, thirdList
o fourthList
se expande correctamente. También probé varias versiones eval
pero ninguna funciona. Cualquier ayuda sería apreciada
#!/bin/bash
touch a.ext1
touch b.ext1
firstList='*.ext1'
ls $firstList
touch a.ext2
touch b.ext2
secondList='*.{ext1,ext2}'
ls $secondList
ls '$secondList'
ls "$secondList"
thirdList=*.{ext1,ext2}
ls $thirdList
ls '$thirdList'
ls "$thirdList"
fourthList="*.{ext1,ext2}"
ls $fourthList
ls '$fourthList'
ls "$fourthList"
Respuesta1
Elcaparazónse expande *
solo si no está entre comillas, cualquier cita detiene la expansión por parte del shell.
Además, una expansión de llave debe estar sin comillas para que el caparazón la expanda.
Este trabajo (usemos echo para ver qué hace el shell):
$ echo *.{ext1,ext2}
a.ext1 b.ext1 a.ext2 b.ext2
Incluso si hay archivos con otros nombres:
$ touch {a,b}.{ext1,ext2} {c,d}.{ext3,ext4} none
ls
a.ext1 a.ext2 b.ext1 b.ext2 c.ext3 c.ext4 d.ext3 d.ext4 none
$ echo *.{ext1,ext2}
a.ext1 b.ext1 a.ext2 b.ext2
¿Por qué funciona eso?
Es importante que entendamos por qué funciona. Es por el orden de expansión. Primero la "Expansión Brace" y luego (la última) la "Expansión de nombre de ruta" (también conocida como expansión global).
Brace --> Parameter (variable) --> Pathname
Podemos desactivar la "Expansión de nombre de ruta" por un momento:
$ set -f
$ echo *.{ext1,ext2}
*.ext1 *.ext2
La "Expansión del nombre de ruta" recibe dos argumentos: *.ext1
y *.ext2
.
$ set +f
$ echo *.{ext1,ext2}
a.ext1 b.ext1 a.ext2 b.ext2
El problema es que no podemos usar una variable para la expansión de llaves.
Se ha explicado muchas veces antes porusando una variable dentro de una "Expansión de llave"
Para expandir una "Expansión de llave" que es el resultado de una "Expansión de variable", debe volver a enviar la línea de comando al shell con eval
.
$ list={ext1,ext2}
$ eval echo '*.'"$list"
Tirante -->Variable--> Globo || -->Abrazadera-->Variables -->globo
........ citado aquí -->^^^^^^ || evaluación ^^^^^^^^^^^^^^^^^^^^^^^^^
Los valores de los nombres de los archivos no generan problemas de ejecución para eval:
$ touch 'a;date;.ext1'
eval echo '*.'"$list"
a;date;.ext1 a.ext1 b.ext1 a.ext2 b.ext2
Pero el valor de $list
podría ser peligroso. Sin embargo, el valor de $list
lo establece el guionista. El guionista tiene el control de eval
: Simplemente no utilice valores establecidos externamente para $list
. Prueba esto:
#!/bin/bash
touch {a,b,c}.ext{1,2}
list=ext{1,2}
eval ls -l -- '*.'"$list"
Una mejor alternativa.
Una alternativa (sin evaluación) esutilizar Bash "Patrones extendidos":
#!/bin/bash
shopt -s extglob
list='@(ext1|ext2)'
ls -- *.$list
Nota: tenga en cuenta que ambas soluciones (eval y patrones) (tal como están escritas) son seguras para nombres de archivos con espacios o líneas nuevas. Pero fallará $list
con espacios, porque $list
no está entre comillas o la evaluación elimina las comillas.
Respuesta2
Considerar:
secondList='*.{ext1,ext2}'
ls $secondList
El problema es eseexpansión de corséestá hechoantes expansión variable. Eso significa que, en lo anterior, nunca se realiza la expansión de la llave.
Esto se debe a que, cuando bash ve por primera vez la línea de comando, no hay llaves. Una vez secondList
ampliado, ya es demasiado tarde.
Lo siguiente funcionará:
$ s='*'
$ ls $s.{ext1,ext2}
a.ext1 a.ext2 b.ext1 b.ext2
Aquí, la línea de comando tiene llaves para queexpansión de corsépuede realizarse como primer paso. Después de eso, el valor de $s
se sustituye en (expansión variable), y por últimoexpansión del nombre de rutaes interpretado.
Documentación
man bash
explica el orden de expansión:
El orden de las expansiones es: expansión de llaves; expansión de tilde, expansión de parámetros y variables, expansión aritmética y sustitución de comandos (realizada de izquierda a derecha); división de palabras; y expansión del nombre de ruta.