Назад | Перейти на главную страницу

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

Мне просто интересно, в чем именно разница между

[[ $STRING != foo ]]

и

[ $STRING != foo ]

кроме того, последний является posix-совместимым, находится в sh, а первый является расширением, найденным в bash.

Есть несколько отличий. На мой взгляд, самые важные из них:

  1. [ встроен в Bash и многие другие современные оболочки. Встроенный [ похоже на test с дополнительным требованием закрытия ]. Встроенные [ и test имитировать функциональность /bin/[ и /bin/test наряду с их ограничениями, чтобы скрипты были обратно совместимы. Исходные исполняемые файлы все еще существуют в основном для соответствия POSIX и обратной совместимости. Запуск команды type [ в Bash указывает, что [ по умолчанию интерпретируется как встроенный. (Заметка: which [ ищет только исполняемые файлы на ДОРОЖКА и эквивалентен type -p [)
  2. [[ не так совместим, он не обязательно будет работать с любым /bin/sh указывает на. Так [[ это более современный вариант Bash / Zsh / Ksh.
  3. Так как [[ встроен в оболочку и не имеет устаревших требований, вам не нужно беспокоиться о разделении слов на основе IFS переменная, чтобы испортить переменные, которые оцениваются как строка с пробелами. Таким образом, вам действительно не нужно заключать переменную в двойные кавычки.

По большей части остальное - просто более приятный синтаксис. Чтобы увидеть больше различий, я рекомендую эту ссылку на ответ на часто задаваемые вопросы: В чем разница между test, [и [[?. На самом деле, если вы серьезно относитесь к написанию сценариев bash, я рекомендую прочитать всю вики, включая FAQ, Ловушки, и Руководство. Тестовый раздел из раздела руководства также объясняет эти различия и почему автор (ы) думает [[ - лучший выбор, если вам не нужно беспокоиться о портативности. Основные причины:

  1. Вам не нужно беспокоиться о цитировании левой части теста, чтобы она действительно считывалась как переменная.
  2. Вам не нужно убегать меньше и больше, чем < > с обратной косой чертой, чтобы они не оценивались как перенаправление ввода, что может действительно испортить некоторые вещи, перезаписав файлы. Это снова возвращается к [[ будучи встроенным. Если [(test) - внешняя программа, оболочка должна будет сделать исключение в способе оценки. < и > только если /bin/test вызывается, что на самом деле не имеет смысла.

Коротко:

[это тупица Встроенный

[[]] - это bash Ключевые слова

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

  $ [ a < b ]
 -bash: b: No such file or directory
  $ [[ a < b ]]

Первый пример возвращает ошибку, потому что bash пытается перенаправить файл b команде [a]. Второй пример действительно делает то, что вы от него ожидаете. Символ <больше не имеет особого значения оператора перенаправления файлов.

Источник: http://mywiki.wooledge.org/BashGuide/CommandsAndArguments

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

Некоторые отличия от Bash 4.3.11:

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

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

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

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

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

      В способе синтаксического анализа команды Bash ничего не меняется.

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

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

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

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

  • <

  • && и ||

    • [[ a = a && b = b ]]: правда, логично и
    • [ a = a && b = b ]: ошибка синтаксиса, && анализируется как разделитель команд И 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 ] Эквивалент POSIX5
  • разбиение слов и генерация имени файла при расширении (split + glob)

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

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

¹ Вдохновленный аналогом [[...]] построить в оболочке Korn

², но не работает для некоторых значений 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 ] было бы эквивалентно.

Одиночный кронштейн т.е. [] совместима ли оболочка POSIX с условным выражением.

Двойные скобки т.е. [[]] - это расширенная (или расширенная) версия стандартной версии POSIX, она поддерживается bash и другими оболочками (zsh, ksh).

В bash для числового сравнения мы используем eq, ne,lt и gt, с двойными скобками для сравнения мы можем использовать ==, !=, <, и > буквально.

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

например:

[ var1 lt var2] #works
[ var1 < var2] #error: var2 No such file or directory 
[ var1 \< var2] #works with escape
[[ var1 < var2]] #works

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