Когда обертывать кавычки вокруг переменной оболочки?

может ли кто-нибудь сказать мне, следует ли мне обертывать кавычки вокруг переменных в скрипте оболочки?

например, следующее правильно:

xdg-open $URL 
[ $? -eq 2 ]

или

xdg-open "$URL"
[ "$?" -eq "2" ]

и если да, то почему?

5 ответов


общее правило: процитируйте его, если он может быть пустым или содержать пробелы (или любые пробелы) или специальные символы (подстановочные знаки). Не цитирование строк с пробелами часто приводит к тому, что оболочка разбивает один аргумент на множество.

$? не нужны кавычки, так как это числовое значение. Ли $URL needs это зависит от того, что вы разрешаете там и хотите ли вы все еще аргумент, если он пуст.

Я, как правило, всегда цитирую строки просто по привычке так безопаснее.


короче говоря, укажите все, где вам не требуется оболочка для выполнения разделения маркеров и расширения подстановочных знаков.

одинарные кавычки защищают текст между ними дословно. Это правильный инструмент, когда вам нужно убедиться, что оболочка не касается строки вообще. Как правило, это механизм цитирования выбора, когда вам не требуется интерполяция переменных.

$ 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-адреса содержат метасимволы оболочки, такие как ? и &). Все, что содержит регулярное выражение, обычно должно цитироваться (то же самое). Все, что содержит значительные пробелы, кроме одиночных пробелов между символами без пробелов, должно быть процитировано (поскольку в противном случае оболочка будет munge пробел в, эффективно, одиночные пробелы и обрезать любые ведущие или конечные пробелы).

когда вы знаете, что переменная может содержать только значение, которое не содержит метасимволы оболочки, цитирование не является обязательным. Таким образом, неупомянутую $? в основном, потому что эта переменная может содержать только одно число. Однако,"$?" также правильно и рекомендуется для общей согласованности и правильности (хотя это моя личная рекомендация, а не широко признанная политика).

значения, которые не являются переменными, в основном следуют тем же правилам, хотя вы также можете избежать любых метасимволов вместо их цитирования. Для общего примера, 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."

смогите быть наклеено совместно спина к спине, формируя одиночную длинную строку после удаления tokenization и цитаты.

$ 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 тоже. Но цикл над совпадением подстановочных знаков является общей проблемой и часто выполняется неправильно.)

переменная, содержащая список токенов для цикла или подстановочный знак для расширения реже встречается, поэтому мы иногда сокращаем "цитировать все, если вы точно не знаете, что делаете".


вот трехточечная формула для котировок в целом:

двойные кавычки

в контекстах, где мы хотим подавить слово расщепление и подстановка. Также в контекстах, где мы хотим буквальном рассматриваться как строка, а не регулярное выражение.

одинарные кавычки

в строковых литералах, где мы хотим подавить интерполяцию и специальную обработку обратных косых черт. Другими словами, ситуации, когда использование double цитаты были бы неуместны.

без кавычек

в случаях, когда мы абсолютно уверены, что там нет слова расщепление или подстановка вопросы или хочу разбиение и подстановка.


примеры

двойные кавычки

  • литеральные строки с пробелами ("StackOverflow rocks!", "Steve's Apple")
  • переменной разложения ("$var", "${arr[@]}")
  • команды заменами ("$(ls)", "`ls`")
  • глобусы, где путь к каталогу или часть имени файла включает пробелы ("/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

для использования переменных в скрипте оболочки используйте "" quoted variables как quoted one означает, что переменная может содержать пробелы или специальный символ, который не повлияет на выполнение вашего скрипта оболочки. Иначе, если вы уверены в отсутствии пробелов или специального символа в имени переменной, вы можете использовать их без " ".

пример:

echo "$url name " -- (может использоваться в любое время)

echo "$url name " -- (не может использоваться при таких ситуации, поэтому примите меры предосторожности перед его использованием)