Как мне избежать подстановочного знака/звездочки в bash?
Вопрос
например.
me$ FOO="BAR * BAR"
me$ echo $FOO
BAR file1 file2 file3 file4 BAR
и используя escape-символ "\":
me$ FOO="BAR \* BAR"
me$ echo $FOO
BAR \* BAR
Я явно делаю что-то глупое.
Как мне получить вывод «BAR * BAR»?
Решение
Кавычек при установке $FOO недостаточно.Вам также необходимо указать ссылку на переменную:
me$ FOO="BAR * BAR"
me$ echo "$FOO"
BAR * BAR
Другие советы
КОРОТКИЙ ОТВЕТ
Как говорили другие, вы всегда должны цитировать переменные, чтобы предотвратить странное поведение.Так что используйте эхо "$фу" вместо того, чтобы просто эхо $фу.
ДЛИННЫЙ ОТВЕТ
Я думаю, что этот пример требует дальнейшего объяснения, поскольку здесь происходит нечто большее, чем может показаться на первый взгляд.
Я понимаю, в чем причина вашего замешательства, потому что после того, как вы запустили свой первый пример, вы, вероятно, подумали, что оболочка, очевидно, делает:
- Расширение параметров
- Расширение имени файла
Итак, из вашего первого примера:
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», поскольку расширение имени файла предотвращается escape-символом.
Так почему же использование $foo не сработало?
Это потому, что происходит третье расширение — Удаление цитат.Из руководства по удалению цитат bash:
После предыдущих расширений все некватурные случаи символов '', '' 'и' '', которые не были результатом одного из вышеуказанных расширений, удаляются.
Итак, что происходит, когда вы вводите команду непосредственно в командную строку, escape-символ не является результатом предыдущего расширения, поэтому BASH удаляет его перед отправкой команде echo, но во втором примере "\*" был результат предыдущего расширения параметра, поэтому он НЕ удаляется.В результате echo получает «\*» и печатает именно это.
Обратите внимание на разницу между первым примером: «*» не входит в число символов, которые будут удалены при удалении кавычек.
Я надеюсь в этом есть смысл.В конце концов вывод тот же - просто используйте кавычки.Я просто решил объяснить, почему экранирование, которое по логике должно работать, если задействовано только расширение параметров и имен файлов, не сработало.
Полное объяснение расширений 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
FOO='BAR * BAR'
echo "$FOO"
echo "$FOO"
Возможно, стоит выработать привычку использовать printf
скорее тогда echo
в командной строке.
В этом примере это не дает особой пользы, но может быть более полезным при более сложном выводе.
FOO="BAR * BAR"
printf %s "$FOO"