Різниця між одинарними та подвійними квадратними дужками в 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.