Почему `|` не трактуется буквально в виде глобуса?

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
Обратите внимание, что в некоторых версиях bash синтаксический анализ extglob (если |он особенный) включен по умолчанию в правой части [[ $var = $pattern ]]. Было бы интересно изолировать версии и shoptконфигурации опций, где это поведение наблюдается - если это только те, где extglobвключено, либо по умолчанию, либо в явной конфигурации, ну, вот и мы.
Чарльз Даффи
2
Кстати, если вы хотите несколько более полно исключить случай, когда символ канала взаимодействует с предыдущей стадией синтаксического анализа (что, я согласен, не происходит, но это не так очевидно для читателя, как могло бы быть), вы бы использовать, pattern='a|b'а затем расширить без $patternкавычек на RHS.
Чарльз Даффи
@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 СЛОВА .

При использовании в [[ $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регулярным выражением. То есть кавычки оболочки имеют побочный эффект удаления специального значения операторов регулярных выражений. Это особенность, поэтому поведение аналогично тому [[ $a = "?" ]], но шаблоны подстановочных знаков (используемые в [[ $a = pattern ]]) являются СЛОВАМИ оболочки (например, используются в globs), а регулярные выражения - нет.

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

Тем не менее, обратите внимание, что в то время как

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

сейчас работает,

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

не делает. Тебе нужно:

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

Который в предыдущих версиях bashнекорректно совпадал с обратной косой чертой. Это было исправлено, но

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

Имеет ли не соответствовать на обратной косой черты , как это следует, например. Потому что bashне может понять, что )находится в скобках, поэтому избегает, )чтобы привести к [^]\)]регулярному выражению, которое соответствует любому символу, кроме ], \и ).

ksh93 есть гораздо худшие ошибки на этом фронте.

Во- zshпервых, это обычное слово оболочки, которое ожидается, и операторы регулярного выражения в кавычках не влияют на значение операторов регулярного выражения.

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

Соответствует a|bрегулярному выражению.

Это означает, что =~можно также добавить к команде [/ test:

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

(также работает в yash. The =~потребности быть процитированные в 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 ]].
Исаак
@isaac, да, это то, что я делаю здесь. a|bэто не оболочка WORD здесь, это a, |и bмаркер. Like echo a|bне выводит a|bили не раскрывает a|bглобус, вам нужно заключить его в кавычки, так |как это специальный символ оболочки, который недопустим в этом контексте. [[ $a = (a|b) ]]будет работать как echo (a|b)будет работать как (a|b)оператор подстановки zsh.
Стефан Шазелас
Формулировка и пояснение к вашему ответу только название bash. Это не вся правда.
Исаак
11

Стандартные шарики ( «расширение имени файла») являются: *, ?, и [ ... ]. |не является допустимым оператором glob в стандартных (не extglob) настройках.

Пытаться:

shopt -s extglob
[[ a = @(a|b) ]] && echo matched
Джефф Шаллер
источник
1
Благодарю. Но почему |буквально не вмешиваться? Почему есть синтаксическая ошибка?
Тим
1
Это не было указано.
Джефф Шаллер
3
В стандартных настройках |не является оператором glob, поэтому не |интерпретируется буквально без кавычек? Так почему же синтаксическая ошибка?
Тим
1
|является управляющим персонажем; он никогда не рассматривается как буквальный символ так же, как буква или цифра.
chepner
3
Потому что в этом режиме оболочка не ожидала символа перенаправления канала в середине еще не закрытого [[]]. [[ $a = aне является допустимой командой, чей вывод может быть передан другому процессу (по крайней мере, так думала оболочка, которую вы пытались сделать).
Джейсон С
5

Если вы хотите соответствие регулярному выражению, тест будет:

[[ "$a" =~ a|b ]]
Мертвая хватка
источник
@Tim Вы должны открывать новые вопросы, а не постоянно редактировать свой текущий вопрос.
садовод
@gardenhead: Мое обновление состоит в том, чтобы уточнить мои вопросы, а не менять их на тот случай, если вы пропустите их. Вторая часть, которую я добавил, состоит в том, чтобы показать пояснения к одному комментарию относительно моего исходного вопроса (почему синтаксическая ошибка) не верна.
Тим