Сначала отделите зш от остальных. Дело не в старых или современных оболочках: 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.
Где вы можете опустить двойные кавычки
Вот все случаи, которые я могу вспомнить в оболочке в стиле Борна, где вы можете написать переменную или подстановку команд без двойных кавычек, и значение интерпретируется буквально.
На правой стороне задания.
var=$stuff
a_single_star=*
Обратите внимание, что вам нужны двойные кавычки после export, потому что это обычное встроенное, а не ключевое слово. Это верно только для некоторых оболочек, таких как dash, zsh (в эмуляции sh), yash или шикарный; bash и ksh оба обрабатывают exportспециально.
export VAR="$stuff"
В caseзаявлении.
case $var in …
Обратите внимание, что вам нужны двойные кавычки в регистре. Разделение слов не происходит в шаблоне падежа, но переменная без кавычек интерпретируется как шаблон, тогда как переменная в кавычках интерпретируется как буквенная строка.
a_star='a*'
case $var in
"$a_star") echo "'$var' is the two characters a, *";;
$a_star) echo "'$var' begins with a";;
esac
В двойных скобках. Двойные скобки имеют специальный синтаксис оболочки.
[[ -e $filename ]]
За исключением того, что вам нужны двойные кавычки, где ожидается шаблон или регулярное выражение: справа от =или ==или !=или =~.
a_star='a*'
if [[ $var == "$a_star" ]]; then echo "'$var' is the two characters a, *"
elif [[ $var == $a_star ]]; then echo "'$var' begins with a"
fi
Вам нужны двойные кавычки, как обычно, в одинарных скобках, [ … ]потому что это обычный синтаксис оболочки (это команда, которая вызывается [). Смотрите одинарные или двойные скобки
В перенаправлении в неинтерактивных оболочках POSIX (нет bash, ни ksh88).
echo "hello world" >$filename
Некоторые оболочки в интерактивном режиме обрабатывают значение переменной как шаблон с подстановочными знаками. POSIX запрещает такое поведение в неинтерактивных оболочках, но некоторые оболочки, включая bash (за исключением режима POSIX) и ksh88 (в том числе, когда он обнаруживается как (предположительно) POSIX shнекоторых коммерческих Unices, таких как Solaris), все еще делают это там ( bashтакже пытается разделить и перенаправление не удается , если этот раскол не + подстановка результатов в точности одно слово), поэтому лучше процитирую целей перенаправлений в shсценарии в случае , если вы хотите , чтобы преобразовать его в bashсценарий когда- нибудь, или запустить его в системе , где shнаходится не соответствует этому пункту, или он может быть получен из интерактивных оболочек.
Внутри арифметического выражения. Фактически, вам нужно оставить кавычки, чтобы переменная была проанализирована как арифметическое выражение.
expr=2*2
echo "$(($expr))"
Тем не менее, вам нужны кавычки вокруг арифметического расширения, так как в большинстве оболочек они разбиваются на слова, как того требует POSIX (!?).
В ассоциативном массиве индекс.
typeset -A a
i='foo bar*qux'
a[foo\ bar\*qux]=hello
echo "${a[$i]}"
Переменная без кавычек и подстановка команд могут быть полезны в некоторых редких случаях:
- Когда значение переменной или вывод команды состоит из списка шаблонов глобуса, и вы хотите расширить эти шаблоны до списка подходящих файлов.
- Если вы знаете, что значение не содержит подстановочных знаков, это
$IFSне было изменено, и вы хотите разделить его на пробельные символы.
- Если вы хотите разделить значение по определенному символу: отключите глобализацию с помощью
set -f, установите IFSсимвол разделителя (или оставьте его в покое, чтобы разделить пробел), затем выполните расширение.
Zsh
В zsh вы можете опускать двойные кавычки большую часть времени, за некоторыми исключениями.
$varникогда не расширяется до нескольких слов, однако расширяется до пустого списка (в отличие от списка, содержащего одно пустое слово), если значением varявляется пустая строка. Контраст:
var=
print -l $var foo # prints just foo
print -l "$var" foo # prints an empty line, then foo
Аналогично, "${array[@]}"расширяется на все элементы массива, а $arrayраспространяется только на непустые элементы.
@Флаг расширения параметра иногда требует двойных кавычек всей подстановки: "${(@)foo}".
Подстановка команды подвергается разделению поля, если не заключена в кавычки: echo $(echo 'a'; echo '*')печатает a *(с одним пробелом), тогда как echo "$(echo 'a'; echo '*')"печатает неизмененную двухстрочную строку. Используйте, "$(somecommand)"чтобы получить вывод команды в одном слове, без финальных строк. Используйте "${$(somecommand; echo _)%?}"для получения точного вывода команды, включая заключительные символы новой строки. Используется "${(@f)$(somecommand)}"для получения массива строк из вывода команды.
SH_WORD_SPLITопции.