Разница между одинарными и двойными квадратными скобками в Bash


162

Я читаю примеры bash, ifно некоторые примеры написаны в квадратных скобках:

if [ -f $param ]
then
  #...
fi

другие с двойными квадратными скобками:

if [[ $? -ne 0 ]]
then
    start looking for errors in yourlog
fi

В чем разница?


3
Вы можете получить свой ответ, посмотрев на ответ на этот вопрос: unix.stackexchange.com/questions/3831/…
Амир Нагизаде,

Ответы:


188

Одиночные []тесты на послеродовые условия.

Double [[]]- это расширение стандарта []и поддерживается bash и другими оболочками (например, zsh, ksh). Они поддерживают дополнительные операции (а также стандартные операции posix). Например: ||вместо -oи регулярное выражение с =~. Более полный список различий можно найти в разделе руководства bash по условным конструкциям .

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


6
Я бы добавил, что если ваш скрипт не начинается с shebang, который явно запрашивает оболочку, которая поддерживает [[ ]](например, bash с #!/bin/bashили #!/usr/bin/env bash), вы должны использовать параметр portable. Сценарии, в которых предполагается, что / bin / sh поддерживает такие расширения, будут работать в таких ОС, как недавние выпуски Debian и Ubuntu, где это не так.
Гордон Дэвиссон

87

Различия в поведении

Протестировано в Bash 4.3.11:

  • Расширение POSIX против Bash:

  • обычная команда против магии

    • [ это просто обычная команда со странным именем.

      ]это просто аргумент, [который не позволяет использовать другие аргументы.

      Ubuntu 16.04 на самом деле имеет исполняемый файл для него, /usr/bin/[предоставляемый coreutils, но встроенная версия bash имеет преимущество.

      Ничто не изменяется в том, как Bash анализирует команду.

      В частности, <происходит перенаправление &&и ||объединение нескольких команд, ( )генерируются подоболочки, если не выполняется экранирование \, и расширение слов происходит как обычно.

    • [[ X ]]это единственная конструкция, которая делает Xбыть разобранным магическим образом. <, &&, ||И ()рассматриваются специально, и правила разбиения на слова различны.

      Есть и другие отличия, как =и =~.

      В Bashese: [это встроенная команда и [[ключевое слово: /ubuntu/445749/whats-the-difference-between-shell-builtin-and-shell-keyword

  • <

  • && и ||

    • [[ a = a && b = b ]]: правда, логично и
    • [ a = a && b = b ]: синтаксическая ошибка, &&анализируется как разделитель команд ANDcmd1 && cmd2
    • [ a = a -a b = b ]: эквивалентно, но не рекомендуется POSIX³
    • [ a = a ] && [ b = b ]: POSIX и надежный эквивалент
  • (

    • [[ (a = a || a = b) && a = b ]]: ложный
    • [ ( a = a ) ]: синтаксическая ошибка, ()интерпретируется как подоболочка
    • [ \( a = a -o a = b \) -a a = b ]: эквивалентно, но ()не поддерживается POSIX
    • { [ a = a ] || [ a = b ]; } && [ a = b ]POSIX эквивалент 5
  • разделение слов и генерация имени файла при расширениях (split + glob)

    • x='a b'; [[ $x = 'a b' ]]: правда, цитаты не нужны
    • x='a b'; [ $x = 'a b' ]: синтаксическая ошибка, расширяется до [ a b = 'a b' ]
    • x='*'; [ $x = 'a b' ]: синтаксическая ошибка, если в текущем каталоге более одного файла.
    • x='a b'; [ "$x" = 'a b' ]: POSIX эквивалент
  • =

    • [[ ab = a? ]]: true, потому что он выполняет сопоставление с образцом ( * ? [это волшебство). Не расширяется до файлов в текущем каталоге.
    • [ ab = a? ]: a?glob расширяется. Так может быть истина или ложь в зависимости от файлов в текущем каталоге.
    • [ ab = a\? ]: false, не глобальное расширение
    • =и ==одинаковы в обоих [и [[, но ==это расширение Bash.
    • case ab in (a?) echo match; esac: POSIX эквивалент
    • [[ ab =~ 'ab?' ]]: false 4 , теряет магию с''
    • [[ ab? =~ 'ab?' ]]: правда
  • =~

    • [[ ab =~ ab? ]]: true, соответствие POSIX расширенному регулярному выражению , ?расширение не глобус
    • [ a =~ a ]: ошибка синтаксиса. Нет эквивалента Bash.
    • printf 'ab\n' | grep -Eq 'ab?': Эквивалент POSIX (только однострочные данные)
    • awk 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' ab 'ab?': POSIX эквивалент.

Рекомендация : всегда используйте [].

Есть POSIX-эквиваленты для каждой [[ ]]конструкции, которую я видел.

Если вы используете [[ ]]вас:

  • потерять мобильность
  • заставьте читателя узнать тонкости другого расширения bash. [это обычная команда со странным именем, никакой особой семантики не требуется.

¹ Вдохновленный эквивалентной [[...]]конструкции в оболочке Корн

² но терпит неудачу для некоторых значений aили b(например, +или index) и выполняет числовое сравнение, если aи bвыглядит как десятичные целые числа. expr "x$a" '<' "x$b"работает вокруг обоих.

³, а также терпит неудачу для некоторых значений aили bкак !или (.

4 в bash 3.2 и выше и при условии, что совместимость с bash 3.1 не включена (как с BASH_COMPAT=3.1)

5 , хотя группировка (здесь с {...;}командой группой вместо (...)которой будет работать ненужную подоболочку) не является необходимым , как ||и &&оболочками операторов (в отличие от ||и && [[...]]операторов или -o/ -a [операторов) имеет одинаковый приоритет. Так [ a = a ] || [ a = b ] && [ a = b ]было бы эквивалентно.


3
« Есть эквиваленты POSIX для каждой [[]] конструкции, которую я видел». То же самое можно сказать и о любом языке Turing Complete на поверхности планеты.
А. Рик

3
@ A.Rick, который был бы верным ответом на все вопросы «Как сделать X на языке Y» ТАК :-) Конечно, в этом утверждении есть «удобно».
Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功

6
Фантастическое резюме. Спасибо за попытку. Однако я не согласен с рекомендацией. Это переносимость по сравнению с более последовательным и мощным синтаксисом. Если вам требуется bash> 4 в вашей среде, то рекомендуется использовать синтаксис [[]].
Бернард

@Downvoters, пожалуйста, объясните, чтобы я мог учиться и улучшать информацию :-)
Сиро Сантилли 法轮功 冠状 病 六四 事件 法轮功

8
Отличный ответ, но я думаю, что Рекомендация : всегда использовать[] следует читать как Мои предпочтения : используйте, []если вы не хотите терять мобильность . Как указано здесь : Если переносимость / соответствие POSIX или BourneShell является проблемой, следует использовать старый синтаксис. Если, с другой стороны, сценарию требуется BASH, Zsh или KornShell, новый синтаксис обычно более гибкий, но не обязательно обратно совместимый. Я предпочел бы пойти, [[ ab =~ ab? ]]если я могу и не беспокоиться о обратной совместимости, чемprintf 'ab' | grep -Eq 'ab?'
MauricioRobayo

14

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

Внутри двойных скобок для проверки состояния (т. Е. [[...]]) нет разницы между использованием =или ==в старых или новых оболочках.

Изменить: Я должен также отметить, что: В bash всегда используйте двойные скобки [[...]], если это возможно, потому что это безопаснее, чем одиночные скобки. Я поясню почему на следующем примере:

if [ $var == "hello" ]; then

если $ var оказывается пустым / пустым, то это то, что видит скрипт:

if [ == "hello" ]; then

который сломает ваш сценарий. Решение состоит в том, чтобы либо использовать двойные скобки, либо всегда не забывать заключать в кавычки переменные ( "$var"). Двойные скобки лучше защитной практики кодирования.


1
Размещение кавычек вокруг всех операций чтения переменных, если у вас нет веских причин не делать этого, является гораздо лучшей защитной практикой кодирования, поскольку она применяется ко всем операциям чтения переменных, а не только к условиям. Ошибка установщика iTunes однажды удаляла файлы людей, если имя жесткого диска содержало пробелы (или что-то в этом роде). Это также решает проблему, которую вы упоминаете.
Чай Т. Рекс


1

Вы можете использовать двойные квадратные скобки для сопоставления легких регулярных выражений, например:

if [[ $1 =~ "foo.*bar" ]] ; then

(если версия bash, которую вы используете, поддерживает этот синтаксис)


6
За исключением того, что вы процитировали шаблон, поэтому он теперь рассматривается как буквенная строка.
ormaaj

очень верно иногда это меня раздражает :)
asf107

1

Руководство Bash говорит:

При использовании с [[, операторы '<' и '>' сортируют лексикографически с использованием текущей локали. Команда test использует порядок ASCII.

(Тестовая команда идентична [])

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