Різниця між одинарними та подвійними квадратними дужками в Bash


161

Я читаю приклади bash, ifале деякі приклади написані з одинарними квадратними дужками:

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

інші з подвійними квадратними дужками:

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

Яка різниця?


3
Ви можете отримати свою відповідь, подивившись відповідь на це питання: unix.stackexchange.com/questions/3831/…
— Амір Нагізаде

Дивіться також serverfault.com/questions/52034/…
— pevik

Відповіді:


188

Одиничні - []це випробування на відповідність оболонці пози.

Подвійні [[]]є розширенням до стандарту []і підтримуються bash та іншими оболонками (наприклад, zsh, ksh). Вони підтримують додаткові операції (як і стандартні операції посівок). Наприклад: ||замість -oі регулярного вирівнювання з =~. Більш повний перелік відмінностей можна знайти в розділі керівництва Баша про умовні конструкції .

Використовуйте, []коли хочете, щоб ваш скрипт переносився через оболонки. Використовуйте, [[]]якщо ви хочете, щоб умовні вирази не підтримувалися []і не потребували переносності.


6
Я додам, що якщо ваш скрипт не починається з шебанга, який явно вимагає оболонки, яка підтримує [[ ]](наприклад, bash з #!/bin/bashабо #!/usr/bin/env bash), ви повинні використовувати переносний варіант. Сценарії, які передбачають, що / bin / sh підтримує розширення на зразок цього, зламається на таких ОС, як недавні випуски Debian і Ubuntu, де це не так.
— Гордон Девіссон

87

Відмінності в поведінці

Тестовано на Bash 4.3.11:

  • Розширення POSIX проти Bash:

  • регулярна команда проти магії

    • [ - це лише звичайна команда зі дивним іменем.

      ]це лише аргумент, [який запобігає використанню подальших аргументів.

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

      Ніщо не змінюється так, як Bash розбирає команду.

      Зокрема, <це перенаправлення &&та ||об'єднання декількох команд, ( )генерування підшаровок, якщо не уникнути \, і розширення слова відбувається як завжди.

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

      Існують також подальші відмінності, як =і =~.

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

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

Рекомендація : завжди використовуйте [].

Існують POSIX-еквіваленти для кожної [[ ]]конструкції, яку я бачив.

Якщо ви використовуєте [[ ]]:

  • втрачають портативність
  • змусити читача дізнатися тонкощі іншого розширення баш. [це лише звичайна команда зі дивним іменем, ніякої спеціальної семантики не задіяно.

¹ Натхненний еквівалентною [[...]]конструкцією в оболонці Корна

², але не вдається для деяких значень 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 ]було б рівнозначно.


3
" Існують еквіваленти POSIX для кожної конструкції [[]], яку я бачив ". Те ж саме можна сказати про будь-яку мову Тюрінга на обличчі планети.
— А. Рік

3
@ A.Рік, це було б вагомою відповіддю на всі питання "Як зробити X мовою Y" ТАКІ питання :-) Звичайно, в цьому твердженні є "зручно".
— Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

6
Фантастичне резюме. Дякую за зусилля. Однак я не згоден з рекомендацією. Це портативність порівняно з більш стійким і потужним синтаксисом. Якщо ви можете вимагати bash> 4 у своїх середовищах, рекомендується синтаксис [[]].
— Бернард

@Downvoters, будь ласка, поясніть, щоб я міг дізнатися та покращити інформацію :-)
— Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

8
Чудова відповідь, але я думаю, що Рекомендація : завжди користуватися[] слід читати як " Мої переваги" : використовуйте, []якщо ви не хочете втрачати портативність . Як зазначено тут : Якщо переносимість / відповідність POSIX або BourneShell викликає занепокоєння, слід використовувати старий синтаксис. Якщо, з іншого боку, сценарій вимагає BASH, Zsh або KornShell, новий синтаксис, як правило, є більш гнучким, але необов'язково сумісним назад. Я вважаю за краще піти, [[ ab =~ ab? ]]якщо я можу і не буду хвилюватися щодо зворотної сумісності, ніжprintf 'ab' | grep -Eq 'ab?'
— MauricioRobayo

14

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

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

Редагувати: Я також повинен зазначити, що: У баші завжди використовуйте подвійні дужки [[...]], якщо можливо, тому що це безпечніше, ніж одинарні дужки. Я проілюструю, чому на наступному прикладі:

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

якщо $ var стане нульовим / порожнім, то це те, що бачить сценарій:

if [ == "hello" ]; then

що порушить ваш сценарій. Рішення - або використовувати подвійні дужки, або завжди пам'ятати, щоб ставити лапки навколо змінних ( "$var"). Подвійні дужки є кращою оборонною практикою кодування.


1
Розміщення лапок навколо всіх зчитуваних змінних, якщо у вас немає дуже вагомих причин цього не робити, це набагато краща оборонна практика кодування, оскільки вона стосується всіх зчитувань змінних, а не лише тих, що мають умови. Помилка інсталятора iTunes одного разу видалила файли людей, якщо ім'я жорсткого диска містило пробіли (або щось подібне). Це також вирішує проблему, яку ви згадуєте.
— Чай Т. Рекс


1

ви можете використовувати подвійні квадратні дужки для зіставлення легких регулярних виразів, наприклад:

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

(поки версія bash, яку ви використовуєте, підтримує цей синтаксис)


6
За винятком того, що ви цитували шаблон, тому він зараз трактується як буквальний рядок.
— ormaaj

дуже правильно. іноді це мене дратує :)
— asf107

1

Посібник Баша говорить:

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

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.