Старый совет заключался в том, чтобы заключать в двойные кавычки любое выражение, включающее $VARIABLE
, по крайней мере, если кто-то хотел, чтобы оболочка интерпретировала его как один отдельный элемент, в противном случае любые пробелы в содержимом $VARIABLE
будут отбрасывать оболочку.
Однако я понимаю, что в более поздних версиях оболочек двойные кавычки больше не всегда нужны (по крайней мере, для целей, описанных выше). Например, в bash
:
% FOO='bar baz'
% [ $FOO = 'bar baz' ] && echo OK
bash: [: too many arguments
% [[ $FOO = 'bar baz' ]] && echo OK
OK
% touch 'bar baz'
% ls $FOO
ls: cannot access bar: No such file or directory
ls: cannot access baz: No such file or directory
С zsh
другой стороны, те же три команды выполняются успешно. Поэтому, основываясь на этом эксперименте, кажется, что в bash
, можно опустить двойные кавычки внутри [[ ... ]]
, но не внутри и [ ... ]
не в аргументах командной строки, тогда как zsh
во всех этих случаях двойные кавычки могут быть опущены.
Но вывод общих правил из примеров, подобных приведенным выше, является случайным предложением. Было бы неплохо увидеть сводку о том, когда двойные кавычки необходимы. Я в первую очередь интересует zsh
, bash
и /bin/sh
.
SH_WORD_SPLIT
опции.Ответы:
Сначала отделите зш от остальных. Дело не в старых или современных оболочках: zsh ведет себя по-разному. Разработчики zsh решили сделать его несовместимым с традиционными оболочками (Bourne, ksh, bash), но проще в использовании.
Во-вторых, гораздо проще использовать двойные кавычки, чем помнить, когда они нужны. Они нужны большую часть времени, поэтому вам нужно учиться, когда они не нужны, а не когда они нужны.
В двух словах, двойные кавычки необходимы везде, где ожидается список слов или шаблон . Они являются необязательными в тех случаях, когда синтаксический анализатор ожидает необработанную строку.
Что происходит без кавычек
Обратите внимание, что без двойных кавычек происходят две вещи.
${foo}
или вывод команды для подобной подстановки команды$(foo)
) разбивается на слова, где бы они ни были.Точнее, результат раскрытия делится на каждый символ, который появляется в значении
IFS
переменной (символ-разделитель). Если последовательность символов-разделителей содержит пробелы (пробел, табуляция или перевод строки), этот пробел считается одним символом; ведущие, конечные или повторяющиеся непробельные разделители приводят к пустым полям. Например, сIFS=" :"
,:one::two : three: :four
создает пустые поля передone
, междуone
иtwo
, и (один) междуthree
иfour
.\[*?
. Если этот шаблон соответствует одному или нескольким именам файлов, шаблон заменяется списком соответствующих имен файлов.Расширение переменной без кавычек
$foo
в разговорной речи называется «оператор split + glob», в отличие от того,"$foo"
которое просто принимает значение переменнойfoo
. То же самое касается подстановки команд:"$(foo)"
подстановка$(foo)
команд, подстановка команд, за которой следует split + glob.Где вы можете опустить двойные кавычки
Вот все случаи, которые я могу вспомнить в оболочке в стиле Борна, где вы можете написать переменную или подстановку команд без двойных кавычек, и значение интерпретируется буквально.
На правой стороне задания.
Обратите внимание, что вам нужны двойные кавычки после
export
, потому что это обычное встроенное, а не ключевое слово. Это верно только для некоторых оболочек, таких как dash, zsh (в эмуляции sh), yash или шикарный; bash и ksh оба обрабатываютexport
специально.В
case
заявлении.Обратите внимание, что вам нужны двойные кавычки в регистре. Разделение слов не происходит в шаблоне падежа, но переменная без кавычек интерпретируется как шаблон, тогда как переменная в кавычках интерпретируется как буквенная строка.
В двойных скобках. Двойные скобки имеют специальный синтаксис оболочки.
За исключением того, что вам нужны двойные кавычки, где ожидается шаблон или регулярное выражение: справа от
=
или==
или!=
или=~
.Вам нужны двойные кавычки, как обычно, в одинарных скобках,
[ … ]
потому что это обычный синтаксис оболочки (это команда, которая вызывается[
). Смотрите одинарные или двойные скобкиВ перенаправлении в неинтерактивных оболочках POSIX (нет
bash
, ниksh88
).Некоторые оболочки в интерактивном режиме обрабатывают значение переменной как шаблон с подстановочными знаками. POSIX запрещает такое поведение в неинтерактивных оболочках, но некоторые оболочки, включая bash (за исключением режима POSIX) и ksh88 (в том числе, когда он обнаруживается как (предположительно) POSIX
sh
некоторых коммерческих Unices, таких как Solaris), все еще делают это там (bash
также пытается разделить и перенаправление не удается , если этот раскол не + подстановка результатов в точности одно слово), поэтому лучше процитирую целей перенаправлений вsh
сценарии в случае , если вы хотите , чтобы преобразовать его вbash
сценарий когда- нибудь, или запустить его в системе , гдеsh
находится не соответствует этому пункту, или он может быть получен из интерактивных оболочек.Внутри арифметического выражения. Фактически, вам нужно оставить кавычки, чтобы переменная была проанализирована как арифметическое выражение.
Тем не менее, вам нужны кавычки вокруг арифметического расширения, так как в большинстве оболочек они разбиваются на слова, как того требует POSIX (!?).
В ассоциативном массиве индекс.
Переменная без кавычек и подстановка команд могут быть полезны в некоторых редких случаях:
$IFS
не было изменено, и вы хотите разделить его на пробельные символы.set -f
, установитеIFS
символ разделителя (или оставьте его в покое, чтобы разделить пробел), затем выполните расширение.Zsh
В zsh вы можете опускать двойные кавычки большую часть времени, за некоторыми исключениями.
$var
никогда не расширяется до нескольких слов, однако расширяется до пустого списка (в отличие от списка, содержащего одно пустое слово), если значениемvar
является пустая строка. Контраст:Аналогично,
"${array[@]}"
расширяется на все элементы массива, а$array
распространяется только на непустые элементы.@
Флаг расширения параметра иногда требует двойных кавычек всей подстановки:"${(@)foo}"
.Подстановка команды подвергается разделению поля, если не заключена в кавычки:
echo $(echo 'a'; echo '*')
печатаетa *
(с одним пробелом), тогда какecho "$(echo 'a'; echo '*')"
печатает неизмененную двухстрочную строку. Используйте,"$(somecommand)"
чтобы получить вывод команды в одном слове, без финальных строк. Используйте"${$(somecommand; echo _)%?}"
для получения точного вывода команды, включая заключительные символы новой строки. Используется"${(@f)$(somecommand)}"
для получения массива строк из вывода команды.источник
echo "$(("$expr"))"
man bash
говорит: выражение обрабатывается так, как если бы оно было в двойных кавычках, но двойные кавычки в скобках специально не обрабатываются.echo
. Возможно, стоит попытаться сделать язык еще более явным (возможно, когда синтаксический анализатор ожидает необработанную строку)