Історія баш-глобусу


11

Чи є історична причина, чому Баш «глобулінг» і регулярні вирази не тотожні? Наприклад, я вважаю, що в Bash [1-2]*збігається все, що починається з 1 або 2, після чого слід будь-що інше, тоді як як звичайний вираз [1-2]*буде відповідати лише послідовності 1s і 2s. Мої сценарії Bash та REGEX foo досить слабкі, і я регулярно стикаюся з проблемами, пов'язаними з цими відмінностями, які мене цікавили, чому вони різні.


3
Чи могли б ви rm -- ^[^.].*\.txt$замість цього зробити rm -- *.txt?
Стефан Шазелас

1
Значна частина ваших запитань торкається цієї теми з lwn: lwn.net/Articles/96687
slm

Існують команди, які працюють на імена файлів і приймають regexp. Наприклад, знайти, 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) була чистою реалізацією з нуля, але вона не відійшла повністю від оболонки Томпсона (оболонка V1 -> V6) і включала функції шкаралупи Машея.

Зокрема, аргументи команд все ще були розділені пробілами, |тепер це новий оператор труби, але ^він як і раніше підтримується як альтернатива (а також пояснює, чому ви це робите, [!a-z]а ні [^a-z]), $1все ж був першим аргументом до сценарію, а зворотний косий рисок все ще був символом втечі . Так багато операторів regexp ( ^\|$) мають спеціальне значення в оболонці.

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

rm *.txt

в кінцевому підсумку працює глобус як:

["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$'

для виключення точкових файлів.

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

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

echo a#.txt

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

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

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

ksh93тепер також може включати в свої кулі regexps (базовий, розширений, схожий на perl або "доповнений") (хоча це досить баггі) і навіть надає інструмент для перетворення між glob і regexp ( 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». У наступному десятилітті з'явилися деякі більш-менш стандартні позначення, зокрема .для довільного символу і ?означати, що попередній символ не є обов'язковим.

Позначення бабуса на бабусі випливає з globкоманди, запровадженої ще в Unix v1 в 1971 році. У той час глобалізація виконувалась окремою програмою; пізніше його перенесли в оболонку. Рання globкоманда ?повинна означати "будь-який один символ" і *"будь-яку послідовність символів". Я не знаю, чому обрали персонажів; ?досить інтуїтивно зрозумілий і, *можливо, був натхненний тим, хто регулярно висловлюється.

Глобінг не мав бути таким загальним, як регулярні вирази, і регулярні вирази на той час були не дуже поширені, тому не було заклику до уніфікації понять. З самого початку були синтаксична несумісні з ?, .і це *означають різні речі в шаблонах імен файлів і в регулярних виразах.

Сучасні оболонки, такі як баш, розширюються на глобальних моделях, але це було поступовим еволюцією, підтримуючи відсталу сумісність. Ksh88 (версія оболонки Корна 1988 року ) представив розширений синтаксис для шаблонів оболонок, який не міг бути таким самим синтаксисом, як звичайні регулярні вирази, але був сильно натхненний ним: *(PATTERN)означати будь-яку кількість повторень PATTERN, @(PATTERN1|PATTERN2)означати " PATTERN1чи PATTERN2", тощо.

Сучасні версії bash (починаючи з 2.02) підтримують розширені шаблони ksh88, якщо ви опублікуєте shopt -s extglobспочатку.


Чи ніколи Bash не підтримував екглоб? Наскільки мені відомо, 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, на відміну від ksh, extglob робить bash сумісним з POSIX, оскільки він не відключений у змінних. В ksh, var='@(*)'; echo $varрозширюється на всі імена файлів у поточному режимі, які починаються з @(та закінчуються так, )як вимагає POSIX, а в bash -O extglobньому - до всіх файлів. (все ж, можна вважати, що поведінка bash має тут більше сенсу (а поведінка ksh - це досить болісно, ​​коли ви хочете мати шаблони змінних)). Цей глобальний синтаксис настільки незручний через це (сумісність POSIX / Bourne). Порівняйте із zsh розширеними кулями.
Стефан Шазелас

@ StéphaneChazelas Це все правда, і мені подобається, як ksh дещо розумний щодо цього. Він рідко вступає в гру, хоча, якщо насправді не обмежений POSIX. Практично кожне використання для розбивання слів замінюється кращими можливостями, а зберігання шаблонів у змінних як і раніше є надзвичайною неприємністю, оскільки вам доведеться спорожнити IFS, вимкнути розширення дужок скрізь, крім баш. Я думаю, що все ще неможливо бути повністю безпечним із збереженими візерунками. Наприклад, ця стара проблема втечі ніколи не була вирішена.
ormaaj

1

Історична причина: ТАК. Довідка:
http://en.wikipedia.org/wiki/Glob_(programming)#Origin

Просто, щоб продемонструвати розбіжність, ось хороший і простий приклад: a*

  • оболонка: все означає, що перший символ є, aа потім все, що завгодно (a, ab, abca ...)
  • регулярний вираз: значення - нульове або більше повторень персонажа a(a, aa, aaa ...)

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

Глобінг, можливо, легше зрозуміти для новачків, але це також менш потужна конструкція.

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