Estoy en un directorio en el que tengo dos archivos de texto:
$ touch test1.txt
$ touch test2.txt
Cuando intento enumerar los archivos (con Bash) usando algún patrón, funciona:
$ ls test?.txt
test1.txt test2.txt
$ ls test{1,2}.txt
test1.txt test2.txt
Sin embargo, cuando un patrón es generado por un comando incluido $()
, solo uno de los patrones funciona:
$ ls $(echo 'test?.txt')
test1.txt test2.txt
$ ls $(echo 'test{1,2}.txt')
ls: cannot access test{1,2}.txt: No such file or directory
¿Que está pasando aqui? ¿Por qué el patrón {1,2}
no funciona?
bash
bash-expansion
Herosław Miraszewski
fuente
fuente
?
está también citado, y consigue expandieron después de$(...)
lo sustituye, pero la expansión de llaves no.$
-expansions:zsh -o globsubst -c 'a=/e*; b={/b*,/v*}; echo $a; echo $b'
.Respuestas:
Es una combinación de dos cosas. Primero, la expansión de llaves no es un patrón que coincida con los nombres de los archivos: es una sustitución puramente textual. Consulte ¿Cuál es la diferencia entre `a [bc] d` (paréntesis) y` a {b, c} d` (llaves)? . En segundo lugar, cuando usa el resultado de una sustitución de comando fuera de las comillas dobles (
ls $(…)
), lo que sucede es solo la coincidencia de patrones (y la división de palabras: el operador "split + glob"), no un análisis completo.Con
ls $(echo 'test?.txt')
, el comandoecho 'test?.txt'
genera la cadenatest?.txt
(con una nueva línea final). La sustitución del comando da como resultado la cadenatest?.txt
(sin una nueva línea final, porque la sustitución del comando elimina las nuevas líneas finales). Esta sustitución sin comillas se divide en palabras, produciendo una lista que consiste en una sola cadenatest?.txt
ya que no hay caracteres de espacio en blanco (más precisamente, no hay caracteres$IFS
) en ella. Cada elemento de esta lista de un elemento se somete a una expansión de comodín condicional, y dado que hay un carácter comodín?
en la cadena, la expansión de comodín ocurre. Dado que el patróntest?.txt
coincide con al menos un nombre de archivo, el elemento de la listatest?.txt
se reemplaza por la lista de nombres de archivo que coinciden con los patrones, produciendo la lista de dos elementos que contienetest1.txt
ytest2.txt
. Finalmentels
se llama con dos argumentostest1
ytest2
.Con
ls $(echo 'test{1,2}')
, el comandoecho 'test{1,2}'
genera la cadenatest{1,2}
(con una nueva línea final). La sustitución del comando da como resultado la cadenatest{1,2}
. Esta sustitución sin comillas se divide en palabras, produciendo una lista que consiste en una sola cadenatest{1,2}
. Cada elemento de esta lista de un elemento experimenta una expansión de comodín condicional, que no hace nada (el elemento se deja como está) ya que no hay caracteres comodín en la cadena. Asíls
se llama con el argumento únicotest{1,2}
.A modo de comparación, esto es lo que sucede con
ls $(echo test{1,2})
. El comandoecho test{1,2}
genera la cadenatest1 test2
(con una nueva línea final). La sustitución del comando da como resultado la cadenatest1 test2
(sin una nueva línea final). Esta sustitución sin comillas sufre división de palabras, produciendo dos cadenastest1
ytest2
. Luego, dado que ninguna de las cadenas contiene un carácter comodín, se dejan solas, por lo quels
se llama con dos argumentostest1
ytest2
.fuente
.txt
en la segunda explicación.La expansión de llaves no ocurrirá después de la sustitución del comando. Puede usar eval para forzar otra ronda de expansión:
Su resultado es:
fuente
Ese problema es muy específico
bash
y es porque decidieronbash
separar la expansión de la llave de la expansión del nombre de archivo (globbing) y realizarla primero, antes de todas las otras expansiones.Desde la página del
bash
manual:En su ejemplo,
bash
solo verá sus llaves después de haber realizado la sustitución del comando (the$(echo ...)
), cuando sea demasiado tarde.Esto es diferente de todos los otros shells, que realizan la expansión de llaves justo antes (y algunos incluso como parte de) la expansión del nombre de ruta (globbing). Eso incluye, pero no se limita a,
csh
dónde se inventaron las expansiones de llaves.Este último ejemplo es el mismo en
csh
,zsh
,ksh93
,mksh
ofish
.Además, observe que la expansión de llaves como parte de globbing también está disponible a través de la
glob(3)
función de biblioteca (al menos en Linux y todos los BSD), y en otras implementaciones independientes (por ejemplo, en perl:)perl -le 'print join " ", <test{1,2}.txt>'
.Por qué eso se hizo de manera diferente
bash
probablemente tenga una historia detrás, pero FWIW no pude encontrar ninguna explicación lógica, y encuentro que todas las racionalizaciones post-hoc no son convincentes.fuente
perl
solía invocarcsh
para expandir globs, por lo que no es sorprendente que todavía reconozca los mismos operadores de globbing quecsh
Por favor, inténtalo:::
ls $ (prueba de eco {1,2} \. txt)
Con una barra invertida. Ahora funciona. También elimine lo que decía el cartel anterior, las comillas. El punto no es para un patrón coincidente, sino que debe tomarse literalmente como período aquí.
fuente
{1,2}
"se comporta como lo hace? La pregunta no es "¿Cómo puedo usar un comando{1,2}
para comportarme de la manera en que?
funciona?" Estás respondiendo la pregunta equivocada. (2) La barra invertida no tiene nada que ver con eso. Su comando funciona de la manera en que lo hace porque ha eliminado las comillas que estaban en el comando en la pregunta.Funciona si eliminas las comillas
fuente
?
).