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

427

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

[[ $STRING != foo ]]

а также

[ $STRING != foo ]

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

0x89
источник
1
Если вам также было интересно использовать вообще без скобок, например, в контексте ifоператора, см. Mywiki.wooledge.org/BashPitfalls#if_.5Bgrep_foo_myfile.5D
Kev
Кроме того, из документов Ubuntu: wiki.ubuntu.com/…
radistao

Ответы:

310

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

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

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

  1. Вам не нужно беспокоиться о цитировании левой части теста, чтобы он действительно читался как переменная.
  2. Вам не нужно экранировать меньше и больше, чем < >с обратными слешами, чтобы они не оценивались как перенаправление ввода, которое может действительно испортить некоторые вещи, перезаписывая файлы. Это снова восходит к тому, чтобы [[быть встроенным. Если [(test) - внешняя программа, оболочка должна будет сделать исключение в способе оценки <и >только в том случае, если она /bin/testвызывается, что на самом деле не имеет смысла.
Кайл Брандт
источник
5
Спасибо, я искал ссылку на часто задаваемые вопросы по bash (не знал об этой странице, спасибо).
0x89
2
Я отредактировал ваш пост с этой информацией, но [и тест выполняются как встроенные. Встроенные функции были разработаны для замены / bin / [и / bin / test, но также должны были воспроизвести ограничения двоичных файлов. Команда 'type [' проверяет, используется ли встроенная функция. 'which [' только ищет исполняемые файлы в PATH и эквивалентен 'type -P ['
klynch
133

Короче говоря:

[Баш строит

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

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

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

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

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

abhiomkar
источник
3
[является командой оболочки POSIX; его не нужно встраивать. ]это просто аргумент, который ищет эта команда, так что синтаксис сбалансирован. Команда является синонимом, за testисключением того, что testне ищет закрытие ].
Каз
81

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

Некоторые отличия в 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 ]: синтаксическая ошибка, &&проанализированная как разделитель команд 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 расширяется. Так может быть true или false в зависимости от файлов в текущем каталоге.
    • [ 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. [это обычная команда со странным именем, никакой особой семантики не требуется.

¹ Вдохновленный эквивалентной [[...]]конструкции в оболочке 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 ]было бы эквивалентно.

Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
источник
Как использовать printf 'ab' | grep -Eq 'ab?'внутри if [ … ]?
meeDamian
1
@meeDamian if ( printf 'ab' | grep -Eq 'a' ); then echo 'a'; fi. []это команда , так же , как grep. ()Не может быть необходимо в этой команде я не уверен: я добавил его из-за |, зависит от того, как Bash разбирает вещи. Если бы не было, |я уверен, что вы можете написать просто if cmd arg arg; then.
Сиро Сантилли 新疆 改造 中心 法轮功 六四 事件
1
@meeDamian да, в этом нет необходимости (): stackoverflow.com/questions/8965509/…
Сиро Сантилли 新疆 改造 中心 法轮功 六四 事件
1
Хороший список! Смотрите также: wiki.ubuntu.com/…
radistao
5

Single Bracket ie []- это оболочка POSIX, соответствующая условному выражению.

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

В Баше, для количественного сравнения мы используем 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
Premraj
источник
4

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

romble
источник