Respuestas:
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:
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 * BAR
en 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
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_opts
porque 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 bash
variable $GLOBIGNORE
para evitar la expansión del nombre de archivo por completo si se aplica al patrón global, o usar el set -f
indicador 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
SELECT * FROM etc.
esta es la única forma en que funciona.
echo "$FOO"
Puede valer la pena acostumbrarse a usar en printf
lugar de echo
en 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"