2012-04-08 5 views
71

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

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

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

или

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

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

+10

Там нет глупых вопросов. Ну, там _are, _ но это не один из них :-) – paxdiablo

+1

См. Также http://unix.stackexchange.com/questions/171346/security-implications-of-forgetting-to-quote-a-variable- in-bash-posix-shells – tripleee

+0

Этот вопрос получает много дубликатов, многие из которых не связаны с переменными, поэтому я переименовал «значение» вместо «переменной». Надеюсь, это поможет большему количеству людей найти эту тему. – tripleee

ответ

45

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

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

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

+0

, чтобы вы могли чередоваться между цитирующими и нецитирующими переменными в ваших сценариях? спасибо за ваш ответ – Cristian

+0

Я думаю, что стоит добавить к этому ответу, каков будет эффект не цитирования переменной с пробелами. – Owen

+0

Вам нужно только указать строковые переменные? – Cristian

40

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

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

$ 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 в unquoted variable.) Для статической строки одинарные кавычки имеют наибольший смысл, хотя здесь используется любая форма цитирования или экранирования.

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. 

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

В стороне, скрипты should usually not use ls for anything. Чтобы развернуть подстановочный знак, просто ... используйте его.

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

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

+0

Это вариант (часть) ответа, который я отправил по [связанному вопросу] (http://stackoverflow.com/questions/25277037/printing-asterisk-in-bash-shell). Я вставляю его здесь, потому что это кратковременно и достаточно четко определено, чтобы стать каноническим вопросом для этой конкретной проблемы. – tripleee

+0

Замечу, что это элемент # 0 и повторяющаяся тема в коллекции http://mywiki.wooledge.org/BashPitfalls общих ошибок Bash. Многие, многие из отдельных элементов в этом списке в основном относятся к этой проблеме. – tripleee

4

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

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

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

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

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

Нет цитирует

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


Примеры

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

  • буквенные строки с пробелами ("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")

Смотрите также:

+3

В соответствии с этими рекомендациями можно получить список файлов в корневом каталоге, написав '' ls ""/"Фраза" все строковые контексты "должна быть более тщательно проверена. –

+4

В '[[]]' цитирование имеет значение в правой части '='/'==' и '= ~': оно делает разницу между интерпретацией строки как шаблона/регулярного выражения или буквально. –

+0

@WilliamPursell: '' ls ""/"' на самом деле эквивалентен 'ls /'. – mklement0

0

Я обычно используют кавычки как "$var" для безопасной, если я не уверен, что $var не содержит пространства.

Я использую $var как простой способ присоединиться к линии:

lines="`cat multi-lines-text-file.txt`" 
echo "$lines"        ## multiple lines 
echo $lines        ## all spaces (including newlines) are zapped