Ответы:
КОРОТКИЙ ОТВЕТ
Как уже говорили другие - вы всегда должны указывать переменные, чтобы избежать странного поведения. Поэтому используйте echo "$ foo" вместо echo $ foo .
ДОЛГО ОТВЕТ
Я думаю, что этот пример требует дальнейшего объяснения, потому что происходит больше, чем может показаться на первый взгляд.
Я вижу, в чём состоит ваше замешательство, потому что после запуска вашего первого примера вы, вероятно, подумали про себя, что оболочка явно делает:
Итак, из вашего первого примера:
me$ FOO="BAR * BAR"
me$ echo $FOO
После расширения параметра эквивалентно:
me$ echo BAR * BAR
И после расширения имени файла эквивалентно:
me$ echo BAR file1 file2 file3 file4 BAR
И если вы просто наберете echo BAR * BAR
в командной строке, вы увидите, что они эквивалентны.
Так что вы, вероятно, подумали про себя: «если я уйду от *, я могу предотвратить расширение имени файла»
Итак, из вашего второго примера:
me$ FOO="BAR \* BAR"
me$ echo $FOO
После расширения параметра должно быть эквивалентно:
me$ echo BAR \* BAR
И после расширения имени файла должно быть эквивалентно:
me$ echo BAR \* BAR
И если вы попытаетесь набрать «echo BAR \ * BAR» непосредственно в командной строке, он действительно выведет «BAR * BAR», потому что расширение имени файла предотвращается экранированием.
Так почему же использование $ foo не работает?
Это происходит потому, что происходит третье расширение - удаление цитаты. Из bash ручное удаление цитаты есть:
После предыдущих расширений удаляются все вхождения без кавычек символов '\', '' 'и' "', которые не являются результатом одного из указанных выше расширений.
Итак, что происходит, когда вы вводите команду непосредственно в командной строке, escape-символ не является результатом предыдущего расширения, поэтому BASH удаляет его перед отправкой в команду echo, но во 2-м примере "\ *" был результат предыдущего расширения параметра, поэтому он НЕ удаляется. В результате эхо получает «\ *», и это то, что он печатает.
Обратите внимание на разницу между первым примером: «*» не входит в символы, которые будут удалены при удалении цитаты.
Я надеюсь это имеет смысл. В конце концов вывод тот же - просто используйте кавычки. Я просто подумал, что объясню, почему экранирование, которое по логике должно сработать, если в игру включены только расширение параметров и имен файлов, не работает.
Для полного объяснения расширений BASH обратитесь к:
http://www.gnu.org/software/bash/manual/bashref.html#Shell-Expansions
Я добавлю немного к этой старой теме.
Обычно вы бы использовали
$ echo "$FOO"
Однако у меня были проблемы даже с этим синтаксисом. Рассмотрим следующий скрипт.
#!/bin/bash
curl_opts="-s --noproxy * -O"
curl $curl_opts "$1"
Эти *
потребности должны быть переданы дословно curl
, но возникают те же проблемы. Приведенный выше пример не будет работать (он будет расширен до имен файлов в текущем каталоге) и не будет работать \*
. Вы также не можете $curl_opts
заключить в кавычки, потому что это будет признано как единственная (недействительная) опция curl
.
curl: option -s --noproxy * -O: is unknown
curl: try 'curl --help' or 'curl --manual' for more information
Поэтому я бы порекомендовал использовать bash
переменную, $GLOBIGNORE
чтобы вообще предотвратить расширение имени файла, если он применяется к глобальному шаблону, или использовать set -f
встроенный флаг.
#!/bin/bash
GLOBIGNORE="*"
curl_opts="-s --noproxy * -O"
curl $curl_opts "$1" ## no filename expansion
Применяя к вашему первоначальному примеру:
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.
, это единственный способ, который работает.
echo "$FOO"
Возможно, стоит привыкнуть использовать printf
вместо echo
командной строки.
В этом примере это не дает большой выгоды, но может быть более полезным при более сложном выводе.
FOO="BAR * BAR"
printf %s "$FOO"