Respostas:
RESPOSTA CURTA
Como outros já disseram - você deve sempre citar as variáveis para evitar comportamentos estranhos. Portanto, use echo "$ foo" em vez de apenas echo $ foo .
RESPOSTA LONGA
Eu acho que esse exemplo merece mais explicações, porque há mais coisas acontecendo do que parece.
Eu posso ver onde está sua confusão, porque depois de executar seu primeiro exemplo, você provavelmente pensou que o shell está obviamente fazendo:
Então, do seu primeiro exemplo:
me$ FOO="BAR * BAR"
me$ echo $FOO
Após a expansão do parâmetro é equivalente a:
me$ echo BAR * BAR
E após a expansão do nome do arquivo é equivalente a:
me$ echo BAR file1 file2 file3 file4 BAR
E se você apenas digitar echo BAR * BAR
na linha de comando, verá que eles são equivalentes.
Então você provavelmente pensou: "se eu escapar do *, posso impedir a expansão do nome do arquivo"
Então, do seu segundo exemplo:
me$ FOO="BAR \* BAR"
me$ echo $FOO
Após a expansão do parâmetro deve ser equivalente a:
me$ echo BAR \* BAR
E após a expansão do nome do arquivo deve ser equivalente a:
me$ echo BAR \* BAR
E se você tentar digitar "echo BAR \ * BAR" diretamente na linha de comando, ele realmente imprimirá "BAR * BAR" porque a expansão do nome do arquivo é impedida pela fuga.
Então, por que usar $ foo não funcionou?
É porque existe uma terceira expansão - Remoção de cotação. A partir do bash, a remoção manual de cotações é:
Após as expansões anteriores, todas as ocorrências não citadas dos caracteres '\', '' 'e' "'que não resultaram de uma das expansões acima são removidas.
Então, o que acontece é que, quando você digita o comando diretamente na linha de comando, o caractere de escape não é o resultado de uma expansão anterior, portanto o BASH o remove antes de enviá-lo ao comando echo, mas no segundo exemplo, o "\ *" era o resultado de uma expansão anterior do parâmetro, portanto NÃO é removida. Como resultado, o eco recebe "\ *" e é isso que imprime.
Observe que a diferença entre o primeiro exemplo - "*" não está incluída nos caracteres que serão removidos pela Remoção de cotação.
Espero que isto faça sentido. No final, a conclusão do mesmo - basta usar aspas. Eu apenas pensei em explicar por que escapar, que logicamente deveria funcionar se apenas a expansão Parameter e Filename estiver em jogo, não funcionou.
Para uma explicação completa das expansões do BASH, consulte:
http://www.gnu.org/software/bash/manual/bashref.html#Shell-Expansions
Vou adicionar um pouco a esse tópico antigo.
Normalmente você usaria
$ echo "$FOO"
No entanto, tive problemas mesmo com essa sintaxe. Considere o seguinte script.
#!/bin/bash
curl_opts="-s --noproxy * -O"
curl $curl_opts "$1"
As *
necessidades devem ser transmitidas literalmente para curl
, mas os mesmos problemas surgirão. O exemplo acima não funcionará (ele será expandido para nomes de arquivos no diretório atual) e nem funcionará \*
. Você também não pode citar $curl_opts
porque será reconhecido como uma opção única (inválida) para curl
.
curl: option -s --noproxy * -O: is unknown
curl: try 'curl --help' or 'curl --manual' for more information
Portanto, eu recomendaria o uso da bash
variável $GLOBIGNORE
para impedir a expansão do nome do arquivo completamente, se aplicado ao padrão global, ou usar o set -f
sinalizador interno.
#!/bin/bash
GLOBIGNORE="*"
curl_opts="-s --noproxy * -O"
curl $curl_opts "$1" ## no filename expansion
Aplicando ao seu exemplo 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.
: essa é a única maneira que funciona.
FOO='BAR * BAR'
echo "$FOO"
echo "$FOO"
Pode valer a pena adquirir o hábito de usar, printf
e não echo
na linha de comando.
Neste exemplo, não oferece muitos benefícios, mas pode ser mais útil com saídas mais complexas.
FOO="BAR * BAR"
printf %s "$FOO"