Чи є подвійні квадратні дужки [[]] кращі над одинарними квадратними дужками [] у Bash?


574

Нещодавно в огляді коду співробітник заявив, що [[ ]]перевагу [ ]конструкції слід використовувати в подібних конструкціях

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

Він не міг дати обґрунтування. Чи є такий?


19
Будьте гнучкими, іноді дозволяє собі слухати рада , не вимагаючи його глибоке пояснення :) Що стосується [[з ним код добре і зрозуміло, але пам'ятаєте той день , коли ви будете порт ваших scriptworks по системі з оболонкою по замовчуванням , який не є bashабо ksh, тощо [- більш бридкий, громіздкий, але працює як AK-47у будь-якій ситуації.
грак

178
@rook Ви можете слухати поради без глибоких пояснень. Але коли ви вимагаєте пояснення і не отримуєте його, зазвичай це червоний прапор. "Довіряй, але перевіряй" і все таке.
Йосип Родін

6
Також дивіться: в чому різниця між операторами Bash [[vs [vs (vs ((? На Unix & Linux SE.
codeforester

1
@rook іншими словами "робіть те, що вам кажуть, і не задайте питань"
гліф

@rook Це не мало сенсу.
Ракіб

Відповіді:


607

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

[[ -e $b ]]

перевірити, чи існує файл. Але з [, ви повинні цитувати $b, тому що він розбиває аргумент і розширює речі на зразок "a*"(де [[це береться буквально). Це також пов'язане з тим, як [може бути зовнішня програма і отримує її аргумент так само нормально, як і будь-яка інша програма (хоча вона також може бути вбудованою, але тоді вона все ще не має цієї спеціальної обробки).

[[також має деякі інші приємні функції, наприклад, відповідність регулярного вираження =~разом з операторами, як вони відомі на мовах, подібних С. Ось хороша сторінка про це: Яка різниця між тестом [і[[ ? і тести Баша


21
Враховуючи, що баш є скрізь у ці дні, я схильний вважати, що це досить чортово портативний. Єдине поширене для мене виняток - на платформах для заняття - але ви, мабуть, хочете докласти особливих зусиль для цього, враховуючи обладнання, яке він працює.
гармати

14
@guns: Дійсно. Я б припустив, що ваше друге речення спростує ваше перше; якщо ви розглядаєте розробку програмного забезпечення в цілому, "баш є скрізь", а "виняток - платформи зайнятих", абсолютно несумісні. busbox широко поширений для вбудованої розробки.
Гонки легкості по орбіті

11
@guns: Навіть якщо bash встановлений у моєму вікні, він може не виконувати сценарій (я, можливо, / bin / sh посилається на якийсь тире-варіант, як це стандартно для FreeBSD та Ubuntu). З Busybox також є додаткові дивацтва: сьогодні стандартні параметри компіляції аналізують, [[ ]]але інтерпретують це як те саме, що і [ ].
сумнівним

59
@dubiousjim: Якщо ви використовуєте лише конструкції bash (як ця), ви повинні мати #! / bin / bash, а не #! / bin / sh.
Нік Маттео

16
Ось чому я змушую використовувати свої сценарії, #!/bin/shале потім перемикаю їх на використання, #!/bin/bashяк тільки я покладаюся на якусь особливість BASH, щоб позначити, що це вже не переносна оболонка Bourne.
Антоній

151

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

Деякі відмінності на 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 еквівалент⁵
  • розбиття слів і генерація імен файлів у розширеннях (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⁴, втрачає магію с ''
    • [[ 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як !або (.

⁴ в bash 3.2 і вище і за умови сумісності з bash 3.1 не включено (як у BASH_COMPAT=3.1)

⁵ хоча угруповання (тут з {...;}командою групою замість (...)якої буде працювати непотрібну подоболочкі) не є необхідним , як ||і &&оболонками операторів (на відміну від ||і && [[...]]операторів або -o/ -a [операторів) має однаковий пріоритет. Так [ a = a ] || [ a = b ] && [ a = b ]було б рівнозначно.


4
Хороші пояснення. Я використовую [з тих же причин.
Гордон

2
В Башезі :) га. Приємне роз'яснення відмінностей та причини дотримуватися POSIX.
Джонатан Комар

56

[[ ]]має більше можливостей - я пропоную ознайомитись з Посібником з розширеного сценарію Bash, щоб отримати додаткову інформацію, зокрема розширений розділ тестових команд у главі 7. Тести .

До речі, як зазначає посібник, [[ ]]був представлений в ksh88 (версія Корну 1988 року).


5
Це далеко не башизм, він вперше був введений в оболонку Корна.
Хенк Лангевельд

6
@Thomas, АБС насправді вважається дуже поганою орієнтиром у багатьох колах; хоча він має велику кількість точної інформації, він, як правило, робить дуже мало уваги, щоб уникнути прояву поганих практик на своїх прикладах, і провів велику частину свого життя без збереження.
Чарльз Даффі

@CharlesDuffy дякую за ваш коментар, чи можете ви назвати хорошу альтернативу. Я не фахівець з сценаріїв оболонок, шукаю посібник, з яким можна проконсультуватися щодо написання сценарію приблизно раз на півроку.
Томас

9
@Thomas, Wooledge BashFAQ та пов’язані з ними вікі - це те, що я використовую; їх активно підтримують жителі каналу Freenode #bash (які, хоча іноді колючі, прагнуть дбати про правильність). BashFAQ № 31, mywiki.wooledge.org/BashFAQ/031 , - це сторінка, безпосередньо пов'язана з цим питанням.
Чарльз Даффі

13

З якого компаратора, тесту, дужки чи подвійної дужки найшвидший? ( http://bashcurescancer.com )

Подвійна дужка - це «складна команда», де як тест, так і одинарна дужка є вбудованими оболонками (а фактично це одна і та ж команда). Таким чином, одна дужка і подвійна дужка виконують різні коди.

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


7
Що з одержимістю найшвидших у скриптах оболонки? Я хочу, щоб вона була найбільш портативною і не могла менше дбати про покращення, яке [[може принести Але тоді я старий пердець школи :-)
Єнс

1
Я думаю, що багато оболонок виявляються вбудованими версіями, [і testнавіть незважаючи на те, що існують і зовнішні версії.
сумнівним

4
@Jens взагалі погоджуюсь: ціль всього сценарію - це (була?) Переносність (інакше ми би кодували і компілювали, а не скрипт) ... два винятки, про які я можу подумати, це: (1) заповнення вкладки (де Сценарії завершення можуть отримати дуже довгий з великою кількістю умовної логіки); та (2) супер-підказки ( PS1=...crazy stuff...та / або $PROMPT_COMMAND); для цього я не хочу помітної затримки у виконанні сценарію.
Майкл

1
Деяка одержимість швидким - просто стиль. За умови рівності, чому б не включити більш ефективну конструкцію коду у свій стиль за замовчуванням, особливо якщо ця конструкція також пропонує більш читабельність? Що стосується портативності, то дуже багато завдань, для яких підходить bash, по суті не є портативними. Наприклад, мені потрібно запустити, apt-get updateякщо минуло більше X годин з часу останнього запуску. Це велике полегшення, коли можна залишити портативність поза вже надто довгим списком обмежень для коду.
Рон Берк

5

Якщо ви дотримуєтесь посібника зі стилю 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

1
Google написав " немає розширення імені шляху ... має місце ", але [[ -d ~ ]]повертає істину (що означає, що це ~було розширено /home/user). Я думаю, що Google повинен був бути більш точним у написанні.
JamesThomasMoon1979

2

Типова ситуація, коли ви не можете скористатися [[в сценарії autotools configure.ac, там дужки мають особливе та інше значення, тому вам доведеться використовувати testзамість [або [[- Зауважте, що тест [є тією ж програмою.


2
З огляду на те, що автоінструменти не є оболонкою POSIX, чому ви коли-небудь сподіваєтесь [визначити її функцією оболонки POSIX?
Гордон

Оскільки сценарій autoconf схожий на скрипт оболонки, він створює скрипт оболонки, і більшість команд оболонки працюють всередині нього.
vy32

-1

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


1
дуже правдивий, і зовсім не несуттєвий. переносність bash для старих версій повинна враховуватися. Люди кажуть, що "bash є всюдисущим і портативним, за винятком, можливо, (вставте сюди езотеричну ОС) " - але, на мій досвід, solaris є однією з тих платформ, де особливу увагу потрібно приділяти портативності: не тільки розглянути старі версії bash на новіша ОС, проблеми / помилки з масивами, функціями тощо; але навіть утиліти (використовувані в сценаріях), такі як tr, sed, awk, tar, мають диваки та особливості на solaris, які вам доведеться обійти.
michael

2
ви маєте рацію ... стільки утиліт, які не POSIX на Solaris, просто подивіться на вихід "df" та аргументи ... Ганьба Sun. Сподіваємось, вона потроху зникає (за винятком Канади).
прибиральник

7
Solaris 2.6 серйозно? Він був випущений в 1997 році, а підтримка закінчилася в 2006 році. Я думаю, якщо ви все ще використовуєте це, то у вас є інші проблеми !. Між іншим, він використовував Bash v2.02, який був тим, хто представив подвійні дужки, тому він повинен працювати навіть на чомусь такому старому. Solaris 10 з 2005 року використовував Bash 3.2.51, а Solaris 11 з 2011 року використовує Bash 4.1.11.
peterh

1
Повторно переносимо сценарій. В системах Linux, як правило, є лише видання кожного інструменту, і це видання GNU. У Solaris у вас зазвичай є вибір між натурним виданням Solaris або виданням GNU (скажімо, Solaris tar vs GNU tar). Якщо ви залежите від конкретних розширень GNU, тоді ви повинні вказати, що у вашому сценарії він може бути переносним. Для Solaris ви робите це, префіксуючи "g", наприклад, `ggrep`, якщо ви хочете GNU grep.
peterh

-2

Коротше кажучи, [[краще, оскільки він не розщеплює інший процес. Жодна дужка або одна дужка не повільніша, ніж подвійна дужка, оскільки вона розвиває інший процес.


8
Test і [- імена для однієї вбудованої команди в bash. Спробуйте скористатися, type [щоб побачити це.
AB

1
@alberge, це правда, але [[, на відміну від [, синтаксис інтерпретується інтерпретатором командного рядка bash. У баші спробуйте набрати текст type [[. unix4linux вірно, що, хоча класичні [тести Bourne-shell визначають новий процес визначення значення істини, [[синтаксис (запозичений з ksh bash, zsh тощо) не робить.
Тім Гілберт

2
@Tim, я не впевнений, про яку оболонку Bourne ви говорите, але [вона вбудована в Bash так само, як і Dash ( /bin/shу всі дистрибутивні Linux-дистрибуції).
AB

1
О, я бачу, що ти маєш на увазі, це правда. Я думав про щось на кшталт, скажімо, / bin / sh на старих системах Solaris або HP / UX, але, звичайно, якщо вам потрібно було бути сумісним з тими, якими ви б не користувались [[або.
Тім Гілберт

1
Оболонка @alberge Bourne - це не Bash (він же Bourne Again SHell ).
kiamlaluno
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.