Спочатку відокремте zsh від решти. Це не справа старих проти сучасних оболонок: zsh поводиться інакше. Дизайнери zsh вирішили зробити його несумісним із традиційними снарядами (Bourne, ksh, bash), але простішими у використанні.
По-друге, набагато простіше використовувати подвійні лапки весь час, ніж згадувати, коли вони потрібні. Вони потрібні більшу частину часу, тому вам потрібно буде вчитися, коли вони не потрібні, а не коли вони потрібні.
Коротше кажучи, подвійні лапки необхідні, де очікується список слів або візерунок . Вони необов’язкові в контекстах, де синтаксичний параметр очікується необмеженим рядком.
Що відбувається без лапок
Зауважте, що без подвійних лапок трапляються дві речі.
- По-перше, результат розширення (значення змінної для заміни параметра на зразок
${foo}
, або вихід команди для заміни команди на зразок $(foo)
) розбивається на слова, де б він не містив пробіл.
Точніше, результат розширення розбивається на кожен символ, який відображається у значенні IFS
змінної (розділовий символ). Якщо послідовність символів роздільника містить пробіл (пробіл, вкладка або новий рядок), пробіл вважається одним символом; провідні, кінцеві або повторні роздільники непробільного простору ведуть до порожніх полів. Наприклад, з IFS=" :"
, :one::two : three: :four
видає порожні поля до one
, між one
і two
, та (єдине) між three
і four
.
- Кожне поле, яке є результатом розщеплення, інтерпретується як глобус (шаблон підстановки), якщо він містить один із символів
\[*?
. Якщо цей шаблон відповідає одному чи більше імен файлів, шаблон замінюється списком відповідних імен файлів.
Розширення без змін, котируемого змінною $foo
, відоме як «оператор розділення + глоба», на відміну від "$foo"
якого просто приймає значення змінної foo
. Те ж саме стосується підстановки команд: "$(foo)"
є підміна команд, $(foo)
це підміна команд, за якою слідує розділення + glob.
Де можна опустити подвійні лапки
Ось всі випадки, про які я можу придуматись в оболонці в стилі Борна, де ви можете написати змінну або підстановку команд без подвійних лапок, а значення інтерпретується буквально.
Праворуч завдання.
var=$stuff
a_single_star=*
Зауважте, що вам потрібні подвійні лапки після export
, тому що це звичайний вбудований, а не ключове слово. Це справедливо лише в деяких оболонках, таких як тире, zsh (в емуляції sh), yash або posh; 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 ви можете опускати подвійні лапки більшість разів, за кількома винятками.
$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
параметр.