Чому `|` не обробляється буквально за глобальним малюнком?


13

Моє запитання випливає з того, як зберігання регулярного виразу в змінній оболонки дозволяє уникнути проблем із цитуванням символів, які є особливими для оболонки? .

  1. Чому виникла помилка:

    $ [[ $a = a|b ]]  
    bash: syntax error in conditional expression: unexpected token `|'
    bash: syntax error near `|b'
    

    Очікується, що всередині [[ ... ]]другого операнда =буде глобальна картина.

    Чи a|bне дійсний шаблон глобалізації? Чи можете ви вказати, яке синтаксичне правило воно порушує?

  2. Деякі коментарі нижче зазначають, що |трактується як труба.

    Тоді зміна =глобального шаблону на =~схему регулярного виразки змусить |працювати

    $ [[ $a =~ a|b ]]

    Я дізнався з Learning Bash p180 у своєму попередньому дописі, який |розпізнається як "труба" на початку інтерпретації, навіть перед будь-якими іншими кроками інтерпретації (включаючи розбір умовних виразів у прикладах). Тож як можна |розпізнати оператора регулярних виразів при використанні =~, не розпізнаючи його як недійсне використання, як і під час використання =? Це змушує мене думати, що помилка синтаксису в частині 1 не означає, що |інтерпретується як труба.

    Кожен рядок, який оболонка читає зі стандартного вводу або сценарію, називається конвеєрним; він містить одну або більше команд, розділених нулем або більше символів труби (|). Для кожного конвеєра, який він читає, оболонка розбиває його на команди, встановлює введення / вивід для трубопроводу, а потім виконує наступні дії для кожної команди (мал. 7-1):

Спасибі.


1
Зверніть увагу , що в деяких версіях Баш, extglob синтаксичний (де |спеціальний) знаходиться на за замовчуванням в правій частині [[ $var = $pattern ]]. Було б цікаво виділити версії та shoptконфігурації опцій, де спостерігається така поведінка - якщо це лише ті, де extglobввімкнено, або за замовчуванням, або за явну конфігурацію, ну, ми є.
Чарльз Даффі

2
До речі, якби ви хотіли дещо більш всебічно виключити випадок символу труби, що заважає попередньому етапу розбору (що, я згоден, не відбувається, але це не так очевидно для читача, як це могло бути), ви б використовувати, pattern='a|b'а потім розгорнути без $patternкотирування на РЗС.
Чарльз Даффі

@CharlesDuffy, який був пункт, досягнутий в добротності & A , який це питання є продовженням до.
Стефан Шазелас

Ааа - контекст має сенс; і ваша відповідь тут непересічна. Дякую за обидва пункти.
Чарльз Даффі

Тіме, будь-яка з наведених нижче відповідей відповідає на ваше запитання? Будь ласка, подумайте про прийняття такого, якщо так. Дякую!
Джефф Шаллер

Відповіді:


13

Немає вагомих причин, чому

[[ $a = a|b ]]

Повідомте про помилку, а не перевіряти, чи є $ a a|bрядком, а [[ $a =~ a|b ]]помилка не повертається.

Єдина причина полягає в тому, що |загалом (зовні і всередині [[ ... ]]) особливий характер. У такому [[ $a =положенні bashочікує тип токена, який є звичайним WORD, як аргументи або цілі перенаправлення в звичайному командному рядку оболонки (але так, як якщо б extglobпараметр був увімкнений з bash 4.1).

( СЛОВО тут я маю на увазі слово в гіпотетичній граматиці оболонки, як те, що описується специфікацією POSIX , тобто те, що оболонка розбиратиметься як один маркер у простому командному рядку оболонки, а не інше визначення слів, як англійська один з послідовності букв або послідовності , які не є інтервали між знаками. foo"bar baz", $(echo x y), два таких СЛОВО с).

У звичайному командному рядку оболонки:

echo a|b

Є чи echo aконвеєр b. a|bце не СЛОВО , це три лексеми: a СЛОВ , |лексема та маркер b WORD .

При використанні в [[ $a = a|b ]], bashочікує WORD , які він отримує ( a), але потім знаходить несподіваний |маркер , який викликає помилку.

Цікаво, bashщо не скаржиться на:

[[ $a = a||b ]]

Оскільки тепер це aмаркер, за яким слідує ||маркер, після чого bвін розбирається так само, як:

[[ $a = a || b ]]

Що є тестуванням того, що $aє aабо що bрядок не порожній.

Тепер у:

[[ $a =~ a|b ]]

bashне може мати одне і те ж правило розбору Наявність того ж правила розбору означатиме, що вищезгадане призведе до помилки і що вам потрібно буде цитувати це, |щоб переконатися a|b, що це одне СЛОВ . Але, починаючи з bash 3.2, якщо ви робите:

[[ $a =~ 'a|b' ]]

Це вже не збігається проти a|bрегулярного виразу, але проти a\|bрегулярного. Тобто, котирування оболонки має побічний ефект від усунення особливого значення операторів regexp. Це особливість, тому поведінка схожа на таку [[ $a = "?" ]], але шаблони підстановок (використовувані в [[ $a = pattern ]]) є оболонними WORDS (наприклад, використовуються в глобусах), тоді як регулярні вирази - ні.

Таким чином , bashмає розглядати всі розширені оператори регулярних виразів, які в іншому випадку зазвичай спеціальні символи оболонки , такі як |, (, )інакше при розборі аргументу =~оператора.

Все-таки зауважте, що поки

 [[ $a =~ (ab)*c ]]

зараз працює,

 [[ $a =~ [)}] ]]

не робить. Тобі потрібно:

 [[ $a =~ [\)}] ]]
 [[ $a =~ [')'}] ]]

Який у попередніх версіях bashневірно відповідав би косому. Це було виправлено, але

 [[ $a =~ [^]')'] ]]

Чи має НЕ відповідати на зворотної косої межі , як це слід, наприклад. Тому що bashне в змозі зрозуміти , що )знаходиться в дужках, тому уникає )привести в [^]\)]регулярному виразі, що збігаються з будь-якого символу , але ], \і ).

ksh93 На цьому фронті є набагато гірші помилки.

В zsh, це звичайне слово оболонки, яке очікується, і цитування операторів regexp не впливає на значення операторів regexp.

[[ $a =~ 'a|b' ]]

Збігається з a|bрегулярним виразом.

Це означає , що =~також можуть бути додані до [/ testкоманді:

[ "$a" '=~' 'a|b' ]
test "$a" '=~' 'a|b'

(також працюйте yash. =~Потрібно вказати, zshяк там =somethingє спеціальний оператор оболонки).

bash 3.1 раніше поводився так zsh. Він змінився в 3.2, імовірно, щоб узгодитись ksh93(навіть незважаючи bashна те, що вперше з'явилася оболонка [[ =~ ]]), але ви все одно можете зробити BASH_COMPAT=31або shopt -s compat31повернутися до попередньої поведінки (за винятком того, що поки [[ $a =~ a|b ]]поверне помилку в bash3.1, вона більше не працює в bash -O compat31нових версіях bash).

Сподіваюсь, це пояснює, чому я сказав, що правила є заплутаними і чому:

[[ $a =~ $var ]]

допомагає в тому числі при переносимості на інші оболонки.


zsh також повідомляє про помилку [[ $a = a|b ]].
NotAnUnixNazi

@isaac, так, саме тут я і роблю. a|bце не оболонка WORD тут, це a, |і bмаркер. Як echo a|bне виводить a|bчи не розширює a|bглобус, вам потрібно цитувати, що |це спеціальний символ оболонки, який недійсний у цьому контексті. [[ $a = (a|b) ]]буде працювати, як echo (a|b)би працював як (a|b)оператор zsh wildcard.
Стефан Шазелас

Формулювання та пояснення у вашій відповіді лише ім'я bash. Це не вся правда.
NotAnUnixNazi

11

Стандартні кульки ( «розширення імені файлу») є: *, ?, і [ ... ]. |не є дійсним глобальним оператором у стандартних (неекстглоб) налаштуваннях.

Спробуйте:

shopt -s extglob
[[ a = @(a|b) ]] && echo matched

1
Спасибі. Але чому це не |інтегрується буквально? Чому виникає помилка синтаксису?
Тім

1
Це не котирувалося.
Jeff Schaller

3
У стандартних налаштуваннях, |чи не глобальний оператор, тому не |тлумачиться буквально без цитування? То чому виникає синтаксична помилка?
Тім

1
|є контрольним персонажем; ніколи не трактується як буквальний символ так само, як буквою чи цифрою.
чепнер

3
Тому що в такому режимі оболонка не очікувала символу перенаправлення труби посеред ще не закритого [[]]. [[ $a = aне є дійсною командою, вихід якої може бути перенесений на інший процес (принаймні, так думала оболонка, яку ви намагалися зробити).
Джейсон C

5

Якщо ви хочете, щоб регулярний вирівнювання відповідав тесту, це:

[[ "$a" =~ a|b ]]

@Tim Вам слід відкривати нові запитання, не редагуючи постійно своє поточне запитання.
садівник

@gardenhead: Моє оновлення полягає в тому, щоб уточнити мої запитання, а не змінювати їх, якщо ви їх пропустите. Друга частина, яку я додав, полягає в тому, щоб показати пояснення одного коментаря щодо мого початкового запитання (чому синтаксична помилка) невірне.
Тім
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.