Когда обернуть кавычки вокруг переменной оболочки?
Может кто-нибудь сказать мне, должен ли я заключать в кавычки переменные в сценарии оболочки?
Например, верно ли следующее:
xdg-open $URL
[ $? -eq 2 ]
или же
xdg-open "$URL"
[ "$?" -eq "2" ]
И если так, то почему?
6 ответов
Общее правило: заключите его в кавычки, если оно может быть пустым или содержать пробелы (или любые пробельные символы) или специальные символы (подстановочные знаки). Не цитирование строк с пробелами часто приводит к тому, что оболочка разбивает один аргумент на множество.
$?
кавычки не нужны, так как это числовое значение. Будь то $URL
Это зависит от того, что вы там разрешаете, и хотите ли вы еще аргумент, если он пуст.
Я склонен всегда цитировать строки просто по привычке, потому что так безопаснее.
Вкратце, процитируйте все, где вам не требуется оболочка для разделения токенов и подстановочных знаков.
Одинарные кавычки защищают текст между ними дословно. Это подходящий инструмент, когда вам нужно убедиться, что оболочка вообще не касается строки. Как правило, это предпочтительный механизм цитирования, когда вам не требуется переменная интерполяция.
$ echo 'Nothing \t in here $will change'
Nothing \t in here $will change
$ grep -F '@&$*!!' file /dev/null
file:I can't get this @&$*!! quoting right.
Двойные кавычки подходят, когда требуется переменная интерполяция. С подходящими адаптациями это также хороший обходной путь, когда вам нужны одинарные кавычки в строке. (Не существует простого способа избежать одиночной кавычки между одинарными кавычками, потому что в одинарных кавычках нет механизма перехода - если бы он был, они бы не цитировали полностью дословно.)
$ echo "There is no place like '$HOME'"
There is no place like '/home/me'
Никакие кавычки не подходят, когда вам специально требуется, чтобы оболочка выполнила разбиение токена и / или расширение по шаблону.
Расщепление токенов;
$ words="foo bar baz"
$ for word in $words; do
> echo "$word"
> done
foo
bar
baz
В отличие от:
$ for word in "$words"; do echo "$word"; done
foo bar baz
(Цикл выполняется только один раз над строкой в кавычках.)
$ for word in '$words'; do echo "$word"; done
$words
(Цикл выполняется только один раз, над литеральной строкой в одинарных кавычках.)
Расширение подстановочного знака:
$ pattern='file*.txt'
$ ls $pattern
file1.txt file_other.txt
В отличие от:
$ ls "$pattern"
ls: cannot access file*.txt: No such file or directory
(Там нет файла с именем буквально file*.txt
.)
$ ls '$pattern'
ls: cannot access $pattern: No such file or directory
(Нет файла с именем $pattern
, или!)
В более конкретных терминах все, что содержит имя файла, обычно должно быть заключено в кавычки (потому что имена файлов могут содержать пробелы и другие метасимволы оболочки). Все, что содержит URL, обычно должно быть заключено в кавычки (потому что многие URL содержат метасимволы оболочки, такие как ?
а также &
). Все, что содержит регулярное выражение, обычно должно быть заключено в кавычки (то же самое). Все, что содержит значительные пробелы, кроме одинарных пробелов между непробельными символами, должно быть заключено в кавычки (потому что в противном случае оболочка превратит пробельные символы в эффективные одиночные пробелы и обрежет любые начальные или конечные пробельные символы).
Когда вы знаете, что переменная может содержать только значение, которое не содержит метасимволов оболочки, заключение в кавычки необязательно. Таким образом, без кавычек $?
в принципе нормально, потому что эта переменная может содержать только одно число. Тем не мение, "$?"
также правильно и рекомендуется для общей последовательности и правильности (хотя это моя личная рекомендация, а не общепризнанная политика).
Значения, которые не являются переменными, в основном следуют тем же правилам, хотя вы можете также избегать любых метасимволов вместо того, чтобы заключать их в кавычки. Для общего примера, URL с &
в нем оболочка будет проанализирована как фоновая команда, если метасимвол не экранирован или не заключен в кавычки:
$ wget http://example.com/q&uack
[1] wget http://example.com/q
-bash: uack: command not found
(Конечно, это также происходит, если URL находится в переменной без кавычек.) Для статической строки наиболее целесообразно использовать одинарные кавычки, хотя любая форма цитирования или экранирования работает здесь.
wget 'http://example.com/q&uack' # Single quotes preferred for a static string
wget "http://example.com/q&uack" # Double quotes work here, too (no $ or ` in the value)
wget http://example.com/q\&uack # Backslash escape
wget http://example.com/q'&'uack # Only the metacharacter really needs quoting
Последний пример также предлагает другую полезную концепцию, которую я люблю называть "цитатами на качелях". Если вам нужно смешивать одинарные и двойные кавычки, вы можете использовать их рядом друг с другом. Например, следующие строки в кавычках
'$HOME '
"isn't"
' where `<3'
"' is."
могут быть вставлены вместе, образуя одну длинную строку после токенизации и удаления кавычек.
$ echo '$HOME '"isn't"' where `<3'"' is."
$HOME isn't where `<3' is.
Это не очень разборчиво, но это обычная техника, и поэтому полезно знать.
Кроме того, сценарии обычно не должны использовать ls
для всего. Чтобы расширить шаблон, просто... используйте его.
$ printf '%s\n' $pattern # not ``ls -1 $pattern''
file1.txt
file_other.txt
$ for file in $pattern; do # definitely, definitely not ``for file in $(ls $pattern)''
> printf 'Found file: %s\n' "$file"
> done
Found file: file1.txt
Found file: file_other.txt
(Цикл совершенно лишний в последнем примере; printf
особенно хорошо работает с несколькими аргументами. stat
тоже. Но зацикливание совпадений с подстановочными знаками является распространенной проблемой, и часто выполняется неправильно.)
Переменная, содержащая список токенов для зацикливания или подстановочный знак для расширения, встречается реже, поэтому мы иногда сокращаем, чтобы "заключить в кавычки все, если вы точно не знаете, что делаете".
Вот трехточечная формула для цитат в целом:
Двойные кавычки
В тех случаях, когда мы хотим подавить расщепление и сглаживание слов. Также в тех случаях, когда мы хотим, чтобы литерал обрабатывался как строка, а не как регулярное выражение.
Одинарные кавычки
В строковых литералах, где мы хотим подавить интерполяцию и специальную обработку обратных слешей. Другими словами, ситуации, в которых использование двойных кавычек было бы неуместным.
Без кавычек
В тех случаях, когда мы абсолютно уверены в том, что нет проблем с разделением или смещением слов, или мы хотим разделить и сузить слово.
Примеры
Двойные кавычки
- буквенные строки с пробелами (
"Stackru rocks!"
,"Steve's Apple"
) - переменные расширения (
"$var"
,"${arr[@]}"
) - подстановки команд (
"$(ls)"
,"`ls`"
) - globs, где путь к каталогу или часть имени файла содержит пробелы (
"/my dir/"*
) - для защиты одинарных кавычек (
"single'quote'delimited'string"
) - Расширение параметров Bash (
"${filename##*/}"
)
Одинарные кавычки
- имена команд и аргументы, в которых есть пробелы
- строковые литералы, которые должны быть исключены интерполяцией (
'Really costs $$!'
,'just a backslash followed by a t: \t'
) - для защиты двойных кавычек (
'The "crux"'
) - литералы регулярных выражений, которые нуждаются в интерполяции для подавления
- использовать кавычки для литералов со специальными символами (
$'\n\t'
) - использовать кавычки оболочки, где нам нужно защитить несколько одинарных и двойных кавычек (
$'{"table": "users", "where": "first_name"=\'Steve\'}'
)
Без кавычек
- вокруг стандартных числовых переменных (
$$
,$?
,$#
так далее.) - в арифметических контекстах, таких как
((count++))
,"${arr[idx]}"
,"${string:start:length}"
- внутри
[[ ]]
выражение, свободное от проблем с разделением слов и глобализацией (это вопрос стиля и мнения могут сильно различаться) - где мы хотим разделения слов (
for word in $words
) - где мы хотим болтаться (
for txtfile in *.txt; do ...
) - где мы хотим
~
быть интерпретированным как$HOME
(~/"some dir"
но нет"~/some dir"
)
Смотрите также:
Я вообще использую цитируемый как "$var"
для безопасности, если я не уверен, что $var
не содержит места
Я использую $var
как простой способ объединения строк:
lines="`cat multi-lines-text-file.txt`"
echo "$lines" ## multiple lines
echo $lines ## all spaces (including newlines) are zapped
Для использования переменных в сценарии оболочки используйте "" переменные в кавычках, так как переменная в кавычках означает, что переменная может содержать пробелы или специальные символы, которые не влияют на выполнение сценария оболочки. Иначе, если вы уверены, что в имени переменной нет пробелов или специальных символов, вы можете использовать их без " ".
Пример:
echo "$ url name" - (Может использоваться всегда)
echo "$ url name" - (не может использоваться в таких ситуациях, поэтому примите меры предосторожности перед его использованием)
Всякий раз, когда плагин https://www.shellcheck.net/ для вашего редактора говорит вам.