¿Cómo escapo el carácter comodín / asterisco en bash?

136

Por ejemplo:

me$ FOO="BAR * BAR"
me$ echo $FOO
BAR file1 file2 file3 file4 BAR

y usando el \carácter de escape:

me$ FOO="BAR \* BAR"
me$ echo $FOO
BAR \* BAR

Obviamente estoy haciendo algo estúpido.

¿Cómo obtengo la salida BAR * BAR?

andyuk
fuente

Respuestas:

139

Citar cuando la configuración $FOOno es suficiente. También debe citar la referencia de variable:

me$ FOO="BAR * BAR"
me$ echo "$FOO"
BAR * BAR
finnw
fuente
8
esto es misterioso, ¿por qué es esto? ¿Que esta pasando?
tofutim
Porque las variables se expanden
Daniel
103

RESPUESTA CORTA

Como han dicho otros, siempre debe citar las variables para evitar comportamientos extraños. Así que use echo "$ foo" en lugar de solo echo $ foo .

RESPUESTA LARGA

Creo que este ejemplo merece más explicaciones porque hay más cosas de lo que parece a primera vista.

Puedo ver dónde entra tu confusión porque después de que ejecutaste tu primer ejemplo, probablemente pensaste que obviamente el shell está haciendo lo siguiente:

  1. Expansión de parámetros
  2. Expansión de nombre de archivo

Entonces, desde tu primer ejemplo:

me$ FOO="BAR * BAR"
me$ echo $FOO

Después de la expansión del parámetro es equivalente a:

me$ echo BAR * BAR

Y después de la expansión del nombre de archivo es equivalente a:

me$ echo BAR file1 file2 file3 file4 BAR

Y si solo escribe echo BAR * BARen la línea de comando, verá que son equivalentes.

Entonces probablemente pensaste "si escapo del *, puedo evitar la expansión del nombre de archivo"

Entonces, de tu segundo ejemplo:

me$ FOO="BAR \* BAR"
me$ echo $FOO

Después de la expansión del parámetro debe ser equivalente a:

me$ echo BAR \* BAR

Y después de la expansión del nombre de archivo debe ser equivalente a:

me$ echo BAR \* BAR

Y si intenta escribir "echo BAR \ * BAR" directamente en la línea de comando, imprimirá "BAR * BAR" porque el escape impide la expansión del nombre de archivo.

Entonces, ¿por qué el uso de $ foo no funcionó?

Es porque hay una tercera expansión que se lleva a cabo: la eliminación de cotizaciones. De la eliminación manual de cotizaciones bash es:

Después de las expansiones anteriores, se eliminan todas las ocurrencias sin comillas de los caracteres '\', '' 'y' "'que no resultaron de una de las expansiones anteriores.

Entonces, lo que sucede es que cuando escribe el comando directamente en la línea de comando, el carácter de escape no es el resultado de una expansión anterior, por lo que BASH lo elimina antes de enviarlo al comando echo, pero en el segundo ejemplo, el "\ *" era el resultado de una expansión previa de parámetros, por lo que NO se elimina. Como resultado, echo recibe "\ *" y eso es lo que imprime.

Tenga en cuenta la diferencia entre el primer ejemplo: "*" no se incluye en los caracteres que se eliminarán mediante Quote Removal.

Espero que esto tenga sentido. Al final, la conclusión es la misma: solo use comillas. Solo pensé en explicar por qué escapar, que lógicamente debería funcionar si solo funciona la expansión de parámetro y nombre de archivo, no funcionó.

Para obtener una explicación completa de las expansiones de BASH, consulte:

http://www.gnu.org/software/bash/manual/bashref.html#Shell-Expansions

mithu
fuente
1
¡Gran respuesta! Ahora no creo que haya hecho una pregunta tan tonta. :-)
andyuk
1
¿Existe alguna utilidad para escapar de los caracteres especiales?
Jigar Joshi
"siempre debes citar las variables para evitar comportamientos extraños" - cuando quieras usarlas como cadenas
Angelo
Consulte también stackoverflow.com/questions/10067266/…
tripleee
Si bien la respuesta de @finnw arriba arriba responde directamente a la pregunta, esta respuesta es mucho mejor para explicar por qué , lo que ayuda mucho más a extrapolar cómo funcionaría el uso en nuestros propios escenarios. Este es el tipo de respuesta que necesitamos ver más.
Nicolas Coombs
54

Agregaré un poco a este viejo hilo.

Usualmente usarías

$ echo "$FOO"

Sin embargo, he tenido problemas incluso con esta sintaxis. Considere el siguiente script.

#!/bin/bash
curl_opts="-s --noproxy * -O"
curl $curl_opts "$1"

Se *debe pasar literalmente a curl, pero surgirán los mismos problemas. El ejemplo anterior no funcionará (se expandirá a nombres de archivo en el directorio actual) y tampoco lo hará \*. Tampoco puede cotizar $curl_optsporque se reconocerá como una opción única (no válida) para curl.

curl: option -s --noproxy * -O: is unknown
curl: try 'curl --help' or 'curl --manual' for more information

Por lo tanto, recomendaría el uso de la bashvariable $GLOBIGNOREpara evitar la expansión del nombre de archivo por completo si se aplica al patrón global, o usar el set -findicador incorporado.

#!/bin/bash
GLOBIGNORE="*"
curl_opts="-s --noproxy * -O"
curl $curl_opts "$1"  ## no filename expansion

Aplicando a su ejemplo original:

me$ FOO="BAR * BAR"

me$ echo $FOO
BAR file1 file2 file3 file4 BAR

me$ set -f
me$ echo $FOO
BAR * BAR

me$ set +f
me$ GLOBIGNORE=*
me$ echo $FOO
BAR * BAR
AlexandreH
fuente
44
Gran explicación, gracias! Mi caso de uso es que SELECT * FROM etc.esta es la única forma en que funciona.
knutole
1
¡Gracias por presentar la solución set -f!
Hachre
5
FOO='BAR * BAR'
echo "$FOO"
tzot
fuente
Esto funciona, pero no es esencial cambiar las comillas simples en la primera línea por comillas dobles.
finnw
1
No, no lo es, pero el hábito de usar comillas simples es preferible cuando va a incluir caracteres especiales de shell y no desea ninguna sustitución.
tzot
3
echo "$FOO"
Rafał Dowgird
fuente
3

Puede valer la pena acostumbrarse a usar en printflugar de echoen la línea de comando.

En este ejemplo, no ofrece muchos beneficios, pero puede ser más útil con resultados más complejos.

FOO="BAR * BAR"
printf %s "$FOO"
Dave Webb
fuente
¿Por qué demonios? printf es un proceso separado (al menos, no está integrado en bash) y el uso de printf que demuestres no tiene ningún beneficio sobre echo.
ddaa
2
printf está incorporado un golpe y me hizo decir en mi respuesta "En este ejemplo no da mucho beneficio, pero puede ser más útil con una producción más compleja.
David Webb