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

387

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

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

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

  • 15
    Будьте гибкими, иногда позволяйте себе выслушать совет, не требуя его глубокого объяснения :) Что касается [[ с ним, код хорош и понятен, но помните тот день, когда вы будете переносить ваши скриптовые работы в систему с оболочкой по умолчанию, которая не является bash или ksh и т. д. [ уродлив, громоздок, но в любой ситуации работает как AK-47 .
  • 109
    @rook Вы можете выслушать совет без глубокого объяснения, конечно. Но когда вы запрашиваете объяснение и не получаете его, обычно это красный флаг. «Доверяй, но проверяй» и все такое.
Показать ещё 1 комментарий
Теги:
if-statement
syntax

7 ответов

500
Лучший ответ

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

[[ -e $b ]]

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

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

  • 15
    Учитывая, что bash повсюду в наши дни, я склонен думать, что он чертовски портативен. Единственное распространенное для меня исключение - на платформах busybox - но вы все равно, возможно, захотите приложить для этого особые усилия, учитывая оборудование, на котором он работает.
  • 12
    @guns: Действительно. Я бы предположил, что ваше второе предложение опровергает ваше первое; Если рассматривать разработку программного обеспечения в целом, то «bash iswhere» и «исключение - платформы busybox» полностью несовместимы. busybox широко распространен для встраиваемых разработок.
Показать ещё 7 комментариев
50

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

Некоторые отличия в 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 ] будет эквивалентно.

  • 2
    Хорошие объяснения. Я использую [ по тем же причинам.
49

[[ ]] имеет больше возможностей - я предлагаю вам ознакомиться с Advanced Bash Scripting Guide для получения дополнительной информации, в частности расширенная тестовая команда в Глава 7. Тесты.

Кстати, в качестве путеводителей, [[ ]] был введен в ksh88 (версия версии Korn 1988 года).

  • 4
    Это далеко не башизм, он впервые был введен в оболочку Корна.
  • 5
    @Thomas, ABS на самом деле считается очень плохой ссылкой во многих кругах; в то время как у него есть много точной информации, он, как правило, очень мало заботится о том, чтобы избежать демонстрации плохих практик в своих примерах, и большую часть своей жизни провел без всякой поддержки.
Показать ещё 2 комментария
8

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

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

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

  • 7
    Что за одержимость самых быстрых скриптов? Я хочу, чтобы он был максимально портативным и не заботился об улучшении [[ может принести. Но тогда я старый пердун старой школы :-)
  • 1
    Я думаю, что многие оболочки подтверждают встроенные версии [ и test хотя внешние версии также существуют.
Показать ещё 2 комментария
1

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

  • 1
    Учитывая, что автоинструменты не являются оболочкой POSIX, с какой стати вы могли бы ожидать [ быть определенным как функция оболочки POSIX?
  • 0
    Потому что сценарий autoconf выглядит как сценарий оболочки, и он создает сценарий оболочки, и большинство команд оболочки работают внутри него.
0

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

  • 5
    Test и [являются именами одной и той же встроенной команды в bash. Попробуйте использовать type [ чтобы увидеть это.
  • 0
    @alberge, это правда, но [[ в отличие от [ , это синтаксис, интерпретируемый интерпретатором командной строки bash. В bash попробуйте набрать type [[ . Unix4linux правильно, что хотя классическая оболочка Bourne [ тесты запускает новый процесс для определения значения истинности, синтаксис [[ заимствован из ksh bash, zsh и т. д.) - нет.
Показать ещё 3 комментария
-1

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

  • 1
    очень верно, и совсем не несущественно. Переносимость bash между старыми версиями должна быть рассмотрена. Люди говорят, что «bash является вездесущим и переносимым, за исключением, может быть, (вставьте здесь эзотерическую ОС) », - но по моему опыту, Solaris - одна из тех платформ, где особое внимание следует уделить переносимости: не только рассматривать более старые версии bash на более новая ОС, проблемы / ошибки с массивами, функциями и т.д .; но даже утилиты (используемые в сценариях), такие как tr, sed, awk, tar имеют странности и особенности на солярисе, которые вам нужно обойти.
  • 1
    вы так правы ... так много утилит не POSIX на Solaris, просто посмотрите на вывод "df" и аргументы ... Позор Sun. Надеюсь, он постепенно исчезает (за исключением Канады).
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню