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

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

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

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

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

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

Ответ 1

Одиночный [] - это тесты условий, совместимые с командами, совместимыми с командами.

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

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

Ответ 2

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

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

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

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

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

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

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

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

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

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

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

      В Bashese: [ является встроенной командой, а [[ является ключевым словом: https://askubuntu.com/questions/445749/whats-the-difference-between-shell-builtin-and-shell-keyword

  • <

  • && и ||

    • [[ a = a && b = b ]]: верно, логично и
    • [ a = a && b = b ]: синтаксическая ошибка, && анализируется как разделитель cmd1 && cmd2 AND cmd1 && 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 ] { [ a = a ] || [ a = b ]; } && [ a = b ] POSIX эквивалент 5
  • разделение слов и генерация имени файла при расширении (split + glob)

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

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

    • [[ ab =~ ab? ]] [[ ab =~ ab? ]]: Верно, 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 хотя группирование (здесь с группой команд {...;} вместо (...) которая запускает ненужную подоболочку) не является необходимым, поскольку ||и операторы && shell (в отличие от операторов || и && [[...]] или операторов -o/-a [) имеют равный приоритет.Итак, [ a = a ] || [ a = b ] && [ a = b ][ a = a ] || [ a = b ] && [ a = b ] будет эквивалентно.

Ответ 3

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

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

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

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

если $var имеет значение null/empty, то это то, что видит script:

if [ == "hello" ]; then

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

Ответ 5

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

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

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

Ответ 6

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

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

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