История Bash Globbing

11

Есть ли историческая причина, почему Bash "globbing" и регулярные выражения не идентичны? Например, я считаю, что в Bash [1-2]*совпадает все, что начинается с 1 или 2, за которым следует что-нибудь еще, в то время как регулярное выражение [1-2]*будет соответствовать только последовательности 1 и 2. Мои скрипты на Bash и REGEX foo оба довольно слабые, и я регулярно сталкиваюсь с проблемами, связанными с этими различиями, и мне было любопытно, почему они различаются.

StrongBad
источник
3
Рассматриваете ли вы делать rm -- ^[^.].*\.txt$вместо rm -- *.txt?
Стефан Шазелас
1
Большая часть ваших Q х затрагиваются в этой теме от LWN: lwn.net/Articles/96687
ОДС
Есть команды, которые работают с именами файлов и принимают регулярные выражения. Например, find find . -regex ".*\.txt$" | xargs rm --или renameдля переименования файлов (это sedдля имен файлов), имейте в виду, что некоторые системы имеют другое rename.
Ctrl-Alt-Delor
@ Richard, я ^[^.].*\.txt$должен был принять во внимание игнорирование файлов точек. Обратите внимание , что -regexявляется GNU расширения, некоторые оболочки , как ksh93 или Zsh можно включить регэкспы в их комках (попробуйте, например: ksh93 -c 'echo ~(E:^[^.].*\.txt$)')
Stéphane Chazelas
2
Этот удар так тщательно следует существующей практике, избегая при этом несовместимых несовместимых изменений и расширений - одна из его сильных сторон.
ormaaj

Ответы:

12

bashПервоначально был разработан в конце 80-х годов как частичный клон kshс некоторыми интерактивными функциями из csh / tcsh.

Источники шатания должны быть найдены в тех ранних оболочках, на которых они основаны.

kshсам по себе является продолжением оболочки Борна. Сама оболочка Bourne (впервые выпущенная в 1979 году в Unix V7) была чистой реализацией с нуля, но она не полностью отходила от оболочки Thompson (оболочка V1 -> V6) и включала функции оболочки Mashey.

В частности, аргументы команды по-прежнему были разделены пробелами, |теперь они были новым оператором канала, но ^все еще поддерживались в качестве альтернативы (а также объясняют, почему вы этого [!a-z]не делаете [^a-z]), $1были по-прежнему первым аргументом в сценарии, а обратная косая черта по-прежнему была escape-символом. , Поэтому многие операторы регулярных выражений ( ^\|$) имеют особое значение в оболочке.

Оболочка Томпсона опиралась на внешнюю утилиту для глобализации. Когда shнайден неупомянута *, [или ?S в команде, было бы запустить команду через glob.

rm *.txt

в итоге запустил glob как:

["glob", "rm", "*.txt"]

и glob заканчивает работу rmсо списком файлов, соответствующих этому шаблону.

grep a.\*b *.txt

будет работать globкак:

["glob", "grep", "a.\252b", "*.txt"]

*Выше цитировались, установив 8 - бит на этом символе, предотвращая globот лечения его в качестве шаблона. globзатем удалит этот бит перед вызовом grep.

Чтобы сделать эквивалент с регулярными выражениями, это было бы:

regexp rm '\.txt$'

Или:

regexp rm '^[^.].*\.txt$'

исключить dot-файлы.

Необходимость избегать операторов, поскольку они удваиваются как специальные символы оболочки, тот факт, что .в именах файлов часто используется оператор регулярного выражения, делает его не очень подходящим для сопоставления имен файлов и сложным для начинающего. В большинстве случаев все, что вам нужно, это подстановочные знаки, которые могут заменить один ( ?) или любое число ( *) символов.

Теперь в разные оболочки добавлены разные операторы сглаживания. В настоящее время глобусы ksh и zsh (и в некоторой степени, bash -O extglobкоторые реализуют подмножество глобусов ksh) функционально эквивалентны регулярным выражениям с синтаксисом, который менее громоздок для использования с именами файлов и текущим синтаксисом оболочки. Например, в zsh(с расширением extendedglob) вы можете сделать:

echo a#.txt

если вы хотите (маловероятно) сопоставить имена файлов, которые состоят из последовательностей, за aкоторыми следует .txt. Проще, чем echo (^a*\.txt$)(здесь использование фигурных скобок как способ изолировать операторы регулярных выражений от операторов оболочки, которые могли бы быть единственными способами, с которыми оболочки могли бы справиться)

echo (foo|bar|<1-20>).(#i)mpg

Для файлов mpg (без учета регистра), базовым именем которых является foo, bar или десятичное число от 1 до 20 ...

ksh93теперь может также включать регулярные выражения (базовые, расширенные, perl-подобные или «расширенные») в свои глобусы (хотя они довольно глючные) и даже предоставляет инструмент для преобразования между глобальными и регулярными выражениями ( printf %R, printf %P):

echo ~(Ei:.*\.txt)

в матч (не скрытый) TXT файлов с E Xtended регулярных выражений, к регистру I nsensitively.

Стефан Шазелас
источник
Классная запись! Вы на самом деле не можете использовать ~(opt:pat)ни для одной из опций с большой буквы. Может быть print -r -- ~(Ei).*\.txt$. Помещение шаблона внутрь кажется полезным только для того, чтобы не включать и не выключать опцию для части шаблона. Как ни странно, вы можете смешивать и сочетать несколько языков шаблонов в одном глобусе. ~(Ki)*.~(E)txt$эквивалентно. (В конце концов, все просто преобразуется в регулярные выражения и передается внутреннему движку регулярных выражений libast).
ormaaj
@ormaaj, ~(Ei:.*\.txt)у меня работает даже с 15-летними версиями вроде ksh93 o +.
Стефан Шазелас
Работает и с одним из моих сохраненных тестовых двоичных файлов (2014-12-24), но я вспоминаю о проблемах с этим. Вещи всегда случайным образом нарушались и снова исправлялись между каждой версией, когда ksh еще коммерчески разрабатывался. Я помню код сопоставления с образцом, являющийся одной из хрупких областей.
ormaaj
@ormaaj, один различались ~(E)xи в ~(E:x)том , что последний якорь (матчи на xтолько тогда , когда прежние матчи на все , что содержит x), которые могут быть своего рода вопрос вы столкнулись с (использование ~(-lr)~(E:x)для удаления прикрепление, ~(E-lr:x)не будет делать). В любом случае, я согласен, что он довольно глючный, даже в последней версии.
Стефан Шазелас
9

Регулярные языки были введены Клини в 1956 году. В оригинальной статье не было полной современной записи для регулярных выражений, но она ввела «звезду Клина», что A*означает «любое количество повторений A». В следующее десятилетие появилось несколько более или менее стандартных обозначений, в частности .для произвольного символа и ?означающих, что предыдущий символ является необязательным.

Глобальная нотация Bash происходит от globкоманды, введенной в Unix v1 в 1971 году. В то время глобализация выполнялась отдельной программой; это было позже перемещено в раковину. Ранняя globкоманда ?должна означать «любой один символ» и *«любую последовательность символов». Я не знаю, почему персонажи были выбраны; ?является довольно интуитивным, и, *возможно, был вдохновлен тем, что в регулярных выражениях.

Глобирование не предназначалось для того, чтобы быть таким же общим, как регулярные выражения, и регулярные выражения не были широко распространены в то время, поэтому не было необходимости объединять понятия. С самого начала были синтаксическая Несовместимостью с ?, .и это *означают разные вещи в шаблонах имен файлов и в регулярных выражениях.

Современные оболочки, такие как bash, расширяют шаблоны глобусов, но это была постепенная эволюция, поддерживающая обратную совместимость. Ksh88 (версия оболочки Korn 1988 года ) ввел расширенный синтаксис для шаблонов оболочки, который не мог быть тем же синтаксисом, что и обычные регулярные выражения, но сильно вдохновлялся им: *(PATTERN)означать любое количество повторений PATTERN, @(PATTERN1|PATTERN2)означать « PATTERN1или PATTERN2», и т.п.

Современные версии bash (начиная с 2.02) поддерживают расширенные шаблоны ksh88, если вы выпустите их shopt -s extglobпервым.

Жиль "ТАК - перестань быть злым"
источник
Bash никогда не поддерживал extglobs? Насколько я знаю, Bash, zsh и {pd, m} ksh с самого начала поддерживали те же самые глобусы, что описаны в руководстве по ksh88. Ksh до сих пор даже не имеет возможности отключать «расширенные» квантификаторы глобуса, и ksh93 является единственным из всех, кто имеет какие-либо расширения помимо того, что было у ksh88.
ormaaj
2
@ormaaj Ksh88 расширенные глобусы и extglobопция были представлены в bash 2.02 где-то в 1998 году. Zsh приобрел ksh_globв серии 3.1 где-то в то же время. У Zsh есть много собственных расширений (некоторые требуют extended_globопции).
Жиль "ТАК - перестань быть злым"
Понимаю. Так что на самом деле было уже достаточно поздно, чтобы оправдать необходимость выбора. (Я думаю, что отключение по умолчанию в наши дни довольно бессмысленно, но интересно.)
ormaaj
1
@ormaaj, обратите внимание, что в bashотличие от kshextglob делает bash не POSIX-совместимым, потому что он не отключен в переменных. В ksh, var='@(*)'; echo $varрасширяется до всех имен файлов в текущем каталоге, которые начинаются @(и заканчиваются в )соответствии с требованиями POSIX, в то время как в bash -O extglobнем распространяются на все файлы. (тем не менее, можно подумать, что поведение bash здесь имеет больше смысла (и поведение ksh - довольно трудная задача, если вы хотите иметь шаблоны в переменных)). Из-за этого этот синтаксис glob очень неудобен (совместимость с POSIX / Bourne). Сравните с расширенными шарами zsh.
Стефан Шазелас
@ StéphaneChazelas Это все правда, и мне нравится, как ksh несколько умен в этом. Это редко входит в игру, хотя на самом деле не ограничен POSIX. Поскольку почти каждое использование для разделения слов заменяется улучшенными функциями, а сохранение шаблонов в переменных в любом случае является чрезвычайно неприятным, поскольку приходится очищать IFS, отключайте расширение скобок везде, кроме bash. Я думаю, что все еще невозможно быть полностью безопасным с сохраненными образцами. Например, эта старая проблема побега так и не была решена.
ormaaj
1

Историческая причина: ДА. Ссылка:
http://en.wikipedia.org/wiki/Glob_(programming)#Origin

Просто чтобы продемонстрировать расхождение, вот хороший и простой пример: a*

  • Оболочка оболочки: значение есть, сначала символ, aа затем все что угодно (a, ab, abca ...)
  • regex: значение есть ноль или более повторений символа a(a, aa, aaa ...)

Я бы с готовностью согласился, что это несоответствие в значении очень запутанно для новых пользователей.

Для новичков, возможно, легче понять глобпинг, но он также является менее мощной конструкцией.

fgeorgatos
источник