Почему расширение параметров с пробелами без кавычек работает в двойных скобках «[[», но не в одинарных скобках «[»?


86

Я запутался с использованием одинарных или двойных скобок. Посмотрите на этот код:

dir="/home/mazimi/VirtualBox VMs"

if [[ -d ${dir} ]]; then
    echo "yep"
fi

Это работает отлично, хотя строка содержит пробел. Но когда я изменяю это на одну скобку:

dir="/home/mazimi/VirtualBox VMs"

if [ -d ${dir} ]; then
    echo "yep"
fi

Это говорит:

./script.sh: line 5: [: /home/mazimi/VirtualBox: binary operator expected

Когда я изменяю это на:

dir="/home/mazimi/VirtualBox VMs"

if [ -d "${dir}" ]; then
    echo "yep"
fi

Работает нормально. Может кто-нибудь объяснить, что происходит? Когда я должен назначать двойные кавычки вокруг переменных, "${var}"чтобы избежать проблем, вызванных пробелами?



Ответы:


83

Одиночная скобка [- это псевдоним testкоманды, а не синтаксис.

Один из недостатков (из многих) одиночной скобки заключается в том, что если один или несколько операндов пытается вычислить, возвращает пустую строку, он будет жаловаться, что ожидал двух операндов (двоичных). Вот почему вы видите, что люди делают [ x$foo = x$blah ], xгарантии того, что операнд никогда не будет оцениваться как пустая строка.

Двойная скобка [[ ]], с другой стороны, является синтаксисом и гораздо более способна, чем [ ]. Как вы выяснили, у него нет проблемы с одним операндом, и он также допускает больше C-подобного синтаксиса с >, <, >=, <=, !=, ==, &&, ||операторами.

Я рекомендую следующее: если ваш переводчик #!/bin/bash, то всегда используйте[[ ]]

Важно отметить, что [[ ]]это поддерживается не всеми оболочками POSIX, однако многие оболочки поддерживают его, например zshи kshв дополнение кbash


16
Проблема с пустой строкой (и многими другими) решается с помощью кавычек. «Х» означает другие проблемы: где операнды могут быть приняты как операторы . Например, когда $fooесть !или (или -n... Эта проблема не подразумевается как проблема с оболочками POSIX, где число аргументов (кроме [и ]) не превышает четырех.
Стефан Шазелас

21
Чтобы уточнить, так [ x$foo = x$blah ]же просто, как неправильно [ $foo = $bar ]. [ "$foo" = "$bar" ]корректен в любой POSIX-совместимой оболочке, [ "x$foo" = "x$bar" ]будет работать в любой Bourne-подобной оболочке, но xэто не для случаев, когда у вас есть пустые строки, но где $fooможет быть !или -n...
Стефан Шазелас

1
Интересная семантика в этом ответе. Я не уверен, что вы имеете в виду под * не * синтаксисом . Конечно, [это не совсем псевдоним для test. Если бы это было так, то testприняли бы закрывающую квадратную скобку. test -n foo ], Но testне [требует , а требует. [идентичен testво всех других отношениях, но это не так, как aliasработает. Bash описывает [и testкак встроенные оболочки , но [[как ключевое слово оболочки .
Кодзиро

1
Хорошо, в bash [встроена оболочка, но / usr / bin / [также является исполняемым файлом. Это традиционно ссылка на / usr / bin / test, но в современном gnu coreutils - это отдельный двоичный файл. Традиционная версия теста проверяет argv [0], чтобы увидеть, вызывается ли он как [и затем ищет соответствие].
Эван

Мой Баш не работает с >=и<=
Студент

52

Команда [это обычная команда. Хотя большинство оболочек предоставляют его как встроенный для эффективности, он подчиняется нормальным синтаксическим правилам оболочки. [в точности эквивалентно test, за исключением того, что [требует в ]качестве последнего аргумента a и testне делает.

Двойные скобки [[ … ]]имеют специальный синтаксис. Они были введены в ksh (через несколько лет [), потому что их использование [может быть проблематичным и [[допускает некоторые новые приятные дополнения, которые используют специальные символы оболочки. Например, вы можете написать

[[ $x = foo && $y = bar ]]

потому что все условное выражение анализируется оболочкой, тогда как [ $x = foo && $y = bar ]сначала будет разделено на две команды [ $x = fooи $y = bar ]разделены &&оператором. Точно так же двойные скобки позволяют такие вещи, как синтаксис сопоставления с образцом, например, [[ $x == a* ]]чтобы проверить, xначинается ли значение с a; в одиночных скобках это расширит a*список файлов, имена которых начинаются с aтекущего каталога. Двойные скобки были впервые введены в ksh и доступны только в ksh, bash и zsh.

Внутри одинарных скобок необходимо использовать двойные кавычки вокруг подстановок переменных, как и в большинстве других мест, потому что они являются просто аргументами команды (которая является [командой). Внутри двойных скобок вам не нужны двойные кавычки, потому что оболочка не разбивает и не разбивает слова: она разбирает условное выражение, а не команду.

Исключением являются случаи, [[ $var1 = "$var2" ]]когда вам нужны кавычки, если вы хотите сделать сравнение строк в байтах, в противном случае $var2это будет образец для $var1сравнения.

Одна вещь, которую вы не можете сделать, [[ … ]]это использовать переменную в качестве оператора. Например, это совершенно законно (но редко полезно):

if [ -n "$reverse_sort" ]; then op=-gt; else op=-lt; fi

if [ "$x" "$op" "$y" ]; then 

В вашем примере

dir="/home/mazimi/VirtualBox VMs"
if [ -d ${dir} ]; then 

команда внутри ifесть [с 4 -х аргументов -d, /home/mazimi/VirtualBox, VMsи ]. Оболочка разбирает -d /home/mazimi/VirtualBoxи потом не знает что делать VMs. Вы должны были бы предотвратить разделение слов, ${dir}чтобы получить правильно сформированную команду.

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

  • в присваивании: foo=$bar(но учтите, что вам нужны двойные кавычки в export "foo=$bar"или в присваиваниях массива, как array=("$a" "$b"));
  • в caseзаявлении case $foo in …:;
  • в двойных скобках , за исключением на правой стороне =или ==оператора (если вы не хотите делать сопоставления с образцом): [[ $x = "$y" ]].

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


1
@StephaneChazelas Моя ошибка. Оказывается, [действительно из Системы III в 1981 году я думал, что она старше.
Жиль

8

Когда я должен назначать двойные кавычки вокруг переменных, "${var}"чтобы избежать проблем, вызванных пробелами?

Подразумевается в этом вопросе

Почему не достаточно хорош?${variable_name}

${variable_name} не означает, что вы думаете, что это делает ...

… Если вы думаете, что это как- то связано с проблемами, вызванными пробелами (в значениях переменных). хорошо для этого:${variable_name}

$ bar=foo
$ bard=Shakespeare
$ echo $bard
Shakespeare
$ echo ${bar}d
food

и ничего больше! 1  не приносит никакой пользы, если вы сразу не следите за ним с помощью символа, который может быть частью имени переменной: буквы (-или-), подчеркивания () или цифры (-). И даже тогда вы можете обойти это:${variable_name}AZaz_09

$ echo "$bar"d
food

Я не пытаюсь препятствовать его использованию - echo "${bar}d"возможно, это лучшее решение здесь, - но я не рекомендую людям полагаться на фигурные скобки вместо кавычек или инстинктивно применять фигурные скобки, а затем спрашивать: «Теперь мне тоже нужны кавычки ? «  Вы всегда должны использовать кавычки, если у вас нет веских причин не делать этого, и вы уверены, что знаете, что делаете.
_________________
1    За исключением, конечно, того факта, что причудливые формы расширения параметров , например, и строятся на синтаксисе. Кроме того , вам нужно использовать , и т.д., чтобы ссылаться на 10 - й, 11 - й и т.д., позиционные параметры - котировки не поможет вам в этом.${parameter:-[word]}${parameter%[word]}${parameter}${10}${11}


2

Для обработки пробелов и пробелов | специальных символов в переменных вы всегда должны заключать их в двойные кавычки. Установка правильного IFS также является хорошей практикой.

Рекомендуется: http://www.dwheeler.com/essays/filenames-in-shell.html

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.