Являются ли двойные квадратные скобки [[]] предпочтительными в квадратных скобках [] в Bash?

Сотрудник недавно заявил в обзоре кода, что конструкция [[ ]] должна быть предпочтительнее [ ] в конструкциях типа

if [ "`id -nu`" = "$someuser" ] ; then 
     echo "I love you madly, $someuser"
fi

Он не мог объяснить. Есть один?

Ответ 1

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

[[ -e $b ]]

проверить, существует ли файл. Но с [ вы должны процитировать $b, потому что он разделяет аргумент и расширяет такие вещи, как "a*" (где [[ берет его буквально). Это также связано с тем, что [ может быть внешней программой и получает свой аргумент как обычно, как и любая другая программа (хотя она также может быть встроенной, но тогда она еще не имеет этой специальной обработки).

[[ также имеет некоторые другие приятные функции, такие как регулярное выражение, соответствующее =~, а также такие операторы, как они известны на языках C-типа. Вот хорошая страница об этом: В чем разница между тестами, [ и [[? и Bash Тесты

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

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

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

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

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

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

¹ Inspired from the equivalent [TG471] construct in the Korn shell

² but fails for some values of [TG472] or [TG473] (like [TG474] or [TG475]) and does numeric comparison if [TG476] and [TG477] look like decimal integers. [TG478] works around both.

³ and also fails for some values of [TG479] or [TG480] like [TG481] or [TG482].

⁴ in bash 3.2 and above and provided compatibility to bash 3.1 is not enabled (like with [TG483])

⁵ though the grouping (here with the [TG484] command group instead of [TG485] which would run an unnecessary subshell) is not necessary as the [TG486] and [TG487] shell operators (as opposed to the [TG488] and [TG489] [TG490] operators or the [TG491]/[TG492] [TG493] operators) have equal precedence. So [TG494] would be equivalent.

Ответ 4

Какой компаратор, тест, скобка или двойная скобка являются самыми быстрыми? (http://bashcurescancer.com)

Двойная скобка - это "составная команда", где в качестве теста и одиночная скобка являются встроенными в оболочку (и на самом деле это одна и та же команда). Таким образом, одиночная скобка и двойная скобка выполняют разные коды.

Тест и одиночная скобка являются наиболее переносимыми, поскольку существуют как отдельные, так и внешние команды. Однако, если вы используете какую-либо удаленно современную версию BASH, двойная скобка поддерживается.

Ответ 5

Типичная ситуация, когда вы не можете использовать [[находится в autotools configure.ac script), там скобки имеют особое и другое значение, поэтому вам нужно будет использовать test вместо [или [[- Примечание этот тест и [являются одной и той же программой.

Ответ 6

Если вам нравится следующее руководство по стилю Google:

Тест, [и [[

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

# This ensures the string on the left is made up of characters in the
# alnum character class followed by the string name.
# Note that the RHS should not be quoted here.
# For the gory details, see
# E14 at https://tiswww.case.edu/php/chet/bash/FAQ
if [[ "filename" =~ ^[[:alnum:]]+name ]]; then
  echo "Match"
fi

# This matches the exact pattern "f*" (Does not match in this case)
if [[ "filename" == "f*" ]]; then
  echo "Match"
fi

# This gives a "too many arguments" error as f* is expanded to the
# contents of the current directory
if [ "filename" == f* ]; then
  echo "Match"
fi

Ответ 7

[[]] двойные скобки не поддерживаются при определенной версии SunOS и полностью неавторизованных внутренних деклараций функций: GNU bash, версия 2.02.0 (1) -release (sparc-sun-solaris2.6)

Ответ 8

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