Сначала отделите зш от остальных. Дело не в старых или современных оболочках: 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
опции.