Чому `[` оболонка вбудована та `[[` ключове слово оболонки?


64

Наскільки я знаю, [[це вдосконалена версія [, але я плутаюся, коли бачу [[це ключове слово і [показують як вбудований.

[root@server ~]# type [
[ is a shell builtin
[root@server ~]# type [[
[[ is a shell keyword

TLDP каже

Вбудований може бути синонімом до однойменної системної команди, але Bash повторно реалізує її. Наприклад, команда Bash echo не є такою ж, як / bin / echo, хоча їх поведінка майже однакова.

і

Ключове слово - це зарезервоване слово, маркер або оператор. Ключові слова мають особливе значення для оболонки, і справді є будівельними блоками синтаксису оболонки. Як приклад, для, в той час, як, і! - це ключові слова. Подібно до вбудованого, ключове слово важко кодується в Bash, але на відміну від вбудованого, ключове слово не є саме по собі командою, а підрозділом конструктивної команди. [2]

Чи не повинно це робити [і [[ключове слово, і Чи є щось, чого мені тут не вистачає? Також це посилання ще раз підтверджує, що обидва [і [[повинні належати до одного виду.



9
/ bin / [існує на моїй машині.
Джошуа

2
В якості простої демонстрації однієї різниці між ними: if "[" $x -eq 3 ]працює , як очікувалося (оскільки Bash виглядає для команди під назвою [, і це є), але if "[[" $x -eq 3 ]]нічого НЕ працює (бо ще раз Bash шукає команду відповідного імені, але немає [[команда).
Кайл Странд

1
@Joshua Так і є /usr/bin/echo, але це не означає, що це не вбудований .
Джонатан Райнхарт

Усі булітини, які також існують зовні, розбирають аргументи, якщо вони не були вбудованими.
Джошуа

Відповіді:


80

Різниця між [і [[досить фундаментальна.

  • [це команда. Його аргументи обробляються так само, як обробляються будь-які інші аргументи команд. Наприклад, врахуйте:

    [ -z $name ]

    Оболонка буде розширюватися $nameі виконувати як розбиття слів, так і генерацію імен файлу за результатом, як і для будь-якої іншої команди.

    Наприклад, не вдасться:

    $ name="here and there"
    $ [ -n $name ] && echo not empty
    bash: [: too many arguments
    

    Для правильної роботи потрібні лапки:

    $ [ -n "$name" ] && echo not empty
    not empty
    
  • [[є ключовим словом оболонки, і його аргументи обробляються за спеціальними правилами. Наприклад, врахуйте:

    [[ -z $name ]]

    Оболонка буде розширюватися, $nameале, на відміну від будь-якої іншої команди, вона не буде виконувати ні розбиття слів, ні генерування імен файлу за результатом. Наприклад, наступне буде успішним, незважаючи на вбудовані пробіли name:

    $ name="here and there"
    $ [[ -n $name ]] && echo not empty
    not empty
    

Підсумок

[ є командою і підпорядковується тим же правилам, що і всі інші команди, які виконує оболонка.

Оскільки [[ключове слово, а не команда, однак оболонка трактує його спеціально, і воно працює за дуже різними правилами.


+1. Дякую. Чи можете ви дати джерело (посилання), за якими правилами працюють команда та ключове слово?
Тім

@Tim Правила, згідно з якими працюють команди , детально описані в man bash. Див., Зокрема, розділи "ПРОСТЕ РОЗШИРЕННЯ КОМАНДИ" та "ВИКОНАННЯ КОМАНДИ". На додаток до [[, інші Баш ключові слова включають if, then, whileі «справа». Загальних правил для ключових слів немає: кожне ключове слово є окремим випадком. man bash включає деталі для кожного.
John1024

62

У V7 Unix - де дебютував оболонка Борна - [він називався test, і він існував лише як /bin/test. Отже, код, який ви сьогодні б написали як:

if [ "$foo" = "bar" ] ; then ...

ви б написали натомість як

if test "$foo" = "bar" ; then ...

Ця друга нотація все ще існує, і я вважаю, що більш зрозуміло, що відбувається: ви викликаєте команду test, яка називається , яка оцінює її аргументи і повертає код стану виходу, який ifвикористовується для вирішення, що робити далі. Ця команда може бути вбудована в оболонку, або це зовнішня програма.¹

[як альтернатива, яка testз'явилася пізніше. Це може бути вбудованим синонімом test, але він також надається як /bin/[у сучасних системах для оболонок, які не мають його як вбудований.

[і testможе бути реалізований за допомогою одного і того ж коду. Це стосується /bin/[і /bin/testв OS X, де це жорсткі посилання на один і той же виконуваний файл. У результаті реалізація повністю ігнорує трейлінг ]: його не потрібно, якщо ви називаєте його як /bin/[, і не скаржиться. якщо ви робите надати його /bin/test.⁴

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

Частина відмінності між "вбудованим" та "ключовим словом" пов'язана з цією історією. Це також відображає той факт, що правила синтаксису для розбору [[виразів різні, як зазначено у відповіді John1024 .⁵


Виноски:

  1. Якщо ви дивитесь на це таким чином, це дає зрозуміти, чому ви повинні розміщувати пробіли [у скриптах оболонки, на відміну від способів, які працюють в дужках та дужках у більшості інших мов програмування. Якщо дозвільний аналізатор команд оболонки дозволив if["$x"..., він також повинен був би дозволитиiftest"$x"...

  2. Це сталося близько 1980 року. /bin/[У моїй копії Ancient Unix V7 від 1979 року не існує, і не є man testдокументом як псевдонім. У відповідного запису чоловік сторінки я маю на пре-реліз копію системи III керівництва з 1980 року, він буде в списку.

  3. ls -i /bin/[ /bin/test

  4. Але не розраховуйте на таку поведінку. Вбудована версія Bash [дійсно вимагає закриття ], і її вбудована testреалізація скаржиться, якщо ви її надаєте.

  5. Відмінність вбудованої та зовнішньої команд може мати значення і з іншої причини: дві реалізації можуть поводитися по-різному. Це стосується echoбагатьох систем. Оскільки існує лише одна реалізація, такого розрізнення для ключового слова не потрібно робити.


Спасибі @Warren Young, але чому builtinі keywordвідмінність між [і , [[коли обидва забезпечує ті ж функціональні можливості ( за винятком того , що [[приходить з великою кількістю функцій , ніж [)?
Срі

2
@sree [[як ключове слово дозволяє bash робити речі, з якими неможливо [- наприклад, цитування не потрібно багато часу, оскільки оболонка знає, що це змінна. Так би мовити, на обробку командного рядка впливає, коли використовується ключове слово, але не коли використовується вбудований - це відбувається набагато пізніше.
муру

1
Зауважте, що код оболонки V7 Bourne містить [вбудований, але код коментується.
Стефан Шазелас

cdє вбудованим, але він нічого не затінює ( cdне може бути реалізований як зовнішня програма).
Paŭlo Ebermann

2
Це чудовий історичний підсумок, але він залишає важливу деталь (IMO), на яку звернули увагу Муру та John1024 у своїй відповіді, що створення [[ключового слова дозволяє оболонці використовувати спеціальні правила розбору для своїх аргументів. Таким чином, на жаль, моє підсумкове питання йде до John1024.
Ільмарі Каронен

3

[спочатку була лише зовнішньою командою, інша назва для /bin/test. Але кілька команд, таких як [і echo, використовуються настільки часто в скриптах оболонки, що виконавці оболонки вирішили скопіювати код безпосередньо в саму оболонку, а не повинні запускати інший процес кожного разу, коли вони використовуються. Це перетворило ці команди на "вбудовані", хоча ви все ще можете викликати зовнішню програму через її повний шлях.

[[з'явилися набагато пізніше. Хоча вбудована реалізована всередині оболонки, вона розбирається так само, як і зовнішні команди. Як пояснено у відповіді John1024, це означає, що без котируваних змінних буде здійснено розбиття слів на них, а лексеми люблять >і <обробляються як перенаправлення вводу / виводу. Це зробило написання складних виразів порівняння незручним. [[був створений як синтаксис оболонки, щоб його можна було ідеосинкратично розібрати. Всередині [[змінних не виходить розділення слів, <і >їх можна використовувати як оператори порівняння, =можуть поводитись по-різному залежно від того, цитується наступний параметр чи ні, і т. Д. Це всі зручності, які [[легше використовувати, ніж традиційна [команда / вбудована.

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

Це схоже на еволюцію, яка спричинила $((...))синтаксис арифметичних виразів, який здебільшого замінив традиційну exprкоманду.


0

Пізні [[в bashце оптимізація в [.

Класика [має один великий недолік, коли її часто використовують для тривіальної операції: вона породжує новий процес кожен раз:
(Він створює новий адресний простір лише для порівняння 0і 1! Кожен раз!)

Я думаю, що головним моментом доповнення [[було те, щоб оцінка вираження всередині [не породила зайвий процес. Але те, як [працювати, змінити не вдалося - це створило б багато плутанини та проблем. Отже, оптимізація була реалізована з новою назвою, більш ефективним способом, а саме командою вбудованої оболонки.
Це стало ключовим словом у синтаксисі оболонки як побічний ефект.

У той час, коли [його використовували спочатку, це був правильний спосіб зробити це із зовнішнім процесом.


5
Зауважте, що, хоча [спочатку була зовнішньою командою, вона була додана як вбудована оболонка досить рано, ймовірно, до моменту виходу Unix System III і, безумовно, до виходу Unix System V. Отже, "додатковий процес" не був проблемою протягом століть. Однак синтаксис залишився незмінним - до нього ставились так, ніби це була зовнішня команда.
Джонатан Леффлер

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