Historia globowania Basha

11

Czy istnieje historyczny powód, dla którego Bash „globbing” i wyrażenia regularne nie są identyczne? Na przykład uważam, że w Bash [1-2]*dopasowuje wszystko, co zaczyna się od 1 lub 2, po których następuje cokolwiek innego, podczas gdy jako wyrażenie regularne [1-2]*pasowałoby tylko do sekwencji 1 i 2. Moje skrypty Bash i REGEX foo są dość słabe i regularnie spotykam się z problemami związanymi z tymi różnicami, co mnie ciekawiło, dlaczego się różnią.

StrongBad
źródło
3
Zastanowiłbyś się rm -- ^[^.].*\.txt$zamiast tego rm -- *.txt?
Stéphane Chazelas,
1
Wiele twoich Q jest poruszonych w tym wątku z lwn: lwn.net/Articles/96687
slm
Istnieją polecenia, które działają na nazwach plików i przyjmują wyrażenie regularne. Na przykład znajdź find . -regex ".*\.txt$" | xargs rm --lub, renameaby zmienić nazwę plików (dotyczy sednazw plików), strzeż się, że niektóre systemy mają inne rename.
ctrl-alt-delor
@richard, miałem ^[^.].*\.txt$wziąć pod uwagę ignorowanie plików kropek. Należy pamiętać, że -regexjest to rozszerzenia GNU, niektóre muszle jak ksh93 lub zsh mogą zawierać wyrażeń regularnych w swoich globs (spróbuj na przykład: ksh93 -c 'echo ~(E:^[^.].*\.txt$)')
Stéphane Chazelas
2
To staranne przestrzeganie istniejącej praktyki, przy jednoczesnym unikaniu niemożliwych do pogodzenia niekompatybilnych zmian i rozszerzeń, jest jedną z jego największych zalet.
ormaaj

Odpowiedzi:

12

bashzostał pierwotnie zaprojektowany pod koniec lat 80. jako częściowy klon kshz niektórymi interaktywnymi funkcjami csh / tcsh.

Początki globowania należy znaleźć we wcześniejszych powłokach, na których się opiera.

kshsam jest przedłużeniem powłoki Bourne'a. Sama powłoka Bourne'a (po raz pierwszy wydana w 1979 roku w Unixie V7) była czystą implementacją od zera, ale nie odbiegała całkowicie od powłoki Thompson (powłoka V1 -> V6) i zawierała funkcje z powłoki Mashey.

W szczególności argumenty poleceń były nadal oddzielone spacjami, |był teraz nowym operatorem potoku, ale ^nadal był obsługiwany jako alternatywa (a także wyjaśnia, dlaczego tak robisz, [!a-z]a nie [^a-z]), $1był nadal pierwszym argumentem skryptu, a ukośnik odwrotny był nadal znakiem ucieczki . Tak wiele operatorów wyrażeń regularnych ( ^\|$) ma specjalne znaczenie w powłoce.

Powłoka Thompsona opierała się na zewnętrznym narzędziu do globowania. Kiedy shznaleziono cytowane *, [lub ?S w poleceniu, by go uruchomić poprzez polecenie glob.

rm *.txt

skończyłby z globem jako:

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

a glob skończyłby rmz listą plików pasujących do tego wzorca.

grep a.\*b *.txt

działałby globjako:

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

*Powyżej zostały podane przez ustawienie 8th trochę na tym charakterze, zapobiegając globod traktując go jako zamiennika. globusunie ten bit przed zadzwonieniem grep.

Aby zrobić odpowiednik wyrażeń regularnych, byłoby to:

regexp rm '\.txt$'

Lub:

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

aby wykluczyć pliki kropkowe.

Konieczność ucieczki przed operatorami, ponieważ podwajają się one jako znaki specjalne powłoki, fakt, że .często w nazwach plików jest operatorem wyrażenia regularnego, sprawia, że ​​dopasowanie nazw plików nie jest zbyt odpowiednie i skomplikowane dla początkującego. W większości przypadków potrzebujesz tylko symboli wieloznacznych, które mogą zastąpić jeden ( ?) lub dowolną liczbę ( *) znaków.

Teraz różne powłoki dodały różnych operatorów globowania. Obecnie globusy ksh i zsh (i do pewnego stopnia bash -O extglobimplementujące podzbiór globów ksh) są funkcjonalnie równoważne wyrażeniom regularnym z składnią, która jest mniej kłopotliwa w użyciu z nazwami plików i bieżącą składnią powłoki. Na przykład w zsh(z rozszerzeniem rozszerzonymglob) możesz:

echo a#.txt

jeśli chcesz (mało prawdopodobne), aby dopasować nazwy plików składające się z sekwencji apo których następuje .txt. Łatwiej niż echo (^a*\.txt$)(tutaj użycie nawiasów klamrowych jako sposobu odizolowania operatorów wyrażeń regularnych od operatorów powłoki, które mogłyby być jednym ze sposobów, w jaki powłoki mogłyby sobie z tym poradzić).

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

Dla plików mpg (bez rozróżniania wielkości liter), których basename to foo, bar lub liczba dziesiętna od 1 do 20 ...

ksh93teraz może również zawierać wyrażenia regularne (podstawowe, rozszerzone, podobne do Perla lub „rozszerzone”) w swoich globach (chociaż jest dość błędne), a nawet zapewnia narzędzie do konwersji między glob i regexp ( printf %R, printf %P):

echo ~(Ei:.*\.txt)

do meczu (nie ukryte) txt plików z E Xtended wyrażeń regularnych, CASE- I nsensitively.

Stéphane Chazelas
źródło
Fajne napisanie! W rzeczywistości nie można użyć ~(opt:pat)żadnej z wielkich liter. Może print -r -- ~(Ei).*\.txt$. Umieszczenie wzoru w środku wydaje się być przydatne tylko w celu uniknięcia konieczności włączania i wyłączania opcji dla części wzoru. Dziwne jest jednak to, że możesz mieszać i dopasowywać wiele języków wzorców w tym samym globu. ~(Ki)*.~(E)txt$jest równoważne. (Na koniec wszystko jest po prostu konwertowane na regex i przekazywane wewnętrznie do silnika regex libast).
ormaaj
@ormaaj, ~(Ei:.*\.txt)działa dla mnie nawet z 15-letnimi wersjami, takimi jak ksh93 o +.
Stéphane Chazelas
Działa również z jednym z moich zapisanych plików binarnych testowych (2014-12-24), ale pamiętam, że mam z tym problemy. Rzeczy zawsze były losowo łamane i naprawiane ponownie między poszczególnymi wersjami, kiedy ksh było nadal komercyjnie rozwijane. Pamiętam, że kod dopasowania wzorca jest jednym z delikatnych obszarów.
ormaaj
@ormaaj, jedna z nich różni się od ~(E)xi ~(E:x)jest to, że ta ostatnia jest zakotwiczona (dopasowuje się xtylko, podczas gdy pierwsza dopasowuje się do wszystkiego, co zawiera x), co może być rodzajem problemu, na jaki się natknąłeś (użyj, ~(-lr)~(E:x)aby usunąć zakotwiczenie, ~(E-lr:x)nie zrobi). W każdym razie zgadzam się, że jest dość wadliwy, nawet w najnowszej wersji.
Stéphane Chazelas
9

Języki regularne zostały wprowadzone przez Kleene w 1956 roku. Artykuł ten nie miał pełnej nowoczesnej notacji dla wyrażeń regularnych, ale wprowadził „gwiazdę Kleen”: A*oznaczającą „dowolną liczbę powtórzeń A”. W następnej dekadzie pojawiły się mniej lub bardziej standardowe notacje, w szczególności .dla dowolnych znaków i ?oznaczające, że poprzedni znak jest opcjonalny.

Notacja globowania Basha wywodzi się z globpolecenia wprowadzonego już w Unixie v1 w 1971 roku. W tym czasie globbing był wykonywany przez osobny program; później został przeniesiony do powłoki. Wczesne globpolecenie musi ?oznaczać „dowolny znak” i *„dowolny ciąg znaków”. Nie wiem, dlaczego wybrano postacie; ?jest dość intuicyjny i *mógł zostać zainspirowany tym z wyrażeń regularnych.

Globbing nie miał być tak ogólny jak wyrażenia regularne, a wyrażenia regularne nie były wówczas bardzo rozpowszechnione, więc nie było wezwania do ujednolicenia pojęć. Od samego początku było składniowe niezgodności z ?, .i *co oznacza różne rzeczy w wzorców nazw plików i wyrażeń regularnych.

Nowoczesne powłoki, takie jak bash, rozwijają się na wzorach globów, ale była to stopniowa ewolucja z zachowaniem kompatybilności wstecznej. Ksh88 (wersja powłoki Korn z 1988 roku ) wprowadziła rozszerzoną składnię dla wzorców powłoki, która nie może być taką samą składnią jak zwykłe wyrażenia regularne, ale jest mocno przez nią zainspirowana: *(PATTERN)oznaczać dowolną liczbę powtórzeń PATTERN, @(PATTERN1|PATTERN2)oznaczać „ PATTERN1lub PATTERN2”, itp.

Nowoczesne wersje bash (od 2.02) obsługują rozszerzone wzorce ksh88, jeśli wydajesz je jako shopt -s extglobpierwsze.

Gilles „SO- przestań być zły”
źródło
Czy Bash kiedykolwiek nie obsługiwał rozszerzeń? O ile mi wiadomo Bash, zsh i {pd, m} ksh od samego początku obsługiwały te same globusy, co udokumentowano w podręczniku ksh88. Ksh do dziś nie ma nawet opcji wyłączania „rozszerzonych” globalnych kwantyfikatorów, a ksh93 jest jedynym z tych pakietów, który ma jakieś rozszerzenia poza tym, co miał ksh88.
ormaaj
2
@ormaaj Ksh88 rozszerzył globusy i extglobopcję wprowadzono w bash 2.02 gdzieś około 1998 roku. Zsh nabył ksh_globw serii 3.1 gdzieś w tym samym czasie. Zsh ma wiele własnych rozszerzeń (niektóre wymagają takiej extended_globopcji).
Gilles „SO- przestań być zły”
Widzę. Tak naprawdę było już wystarczająco późno, aby uzasadnić potrzebę wyboru. (Myślę, że domyślnie wyłączenie jest obecnie bezcelowe, ale ciekawe.)
ormaaj,
1
@ormaaj, Zauważ, że w bashprzeciwieństwie do kshextglob sprawia, że ​​bash nie jest zgodny z POSIX, ponieważ nie jest wyłączony w zmiennych. In ksh, var='@(*)'; echo $varrozwija się do wszystkich nazw plików w bieżącym katalogu, które zaczynają się @(i kończą, )jak wymaga POSIX, podczas gdy w bash -O extglobnim rozwija się do wszystkich plików. (nadal można uznać, że zachowanie bash ma tutaj większy sens (a zachowanie ksh jest dość uciążliwe, gdy chcesz mieć wzorce w zmiennych)). Ta składnia globalna jest z tego powodu tak niewygodna (kompatybilność z POSIX / Bourne). Porównaj z rozszerzonymi globami zsh.
Stéphane Chazelas
@ StéphaneChazelas To wszystko prawda i podoba mi się, że ksh jest w tym trochę mądry. Rzadko pojawia się w grze, chyba że jest ograniczony do POSIX. Ponieważ prawie każde zastosowanie dzielenia słów zastępowane jest lepszymi funkcjami, a przechowywanie wzorców w zmiennych jest i tak wyjątkowo uciążliwe, ponieważ musisz opróżnić IFS, wyłączaj nawiasy klamrowe wszędzie oprócz bash. Myślę, że wciąż nie można być całkowicie bezpiecznym z przechowywanymi wzorami. Na przykład ten stary problem ucieczki nigdy nie został rozwiązany.
ormaaj
1

Powód historyczny: TAK. Odniesienie:
http://en.wikipedia.org/wiki/Glob_(programming)#Origin

Aby pokazać rozbieżność, oto dobry i łatwy przykład: a*

  • shell globbing: oznacza, że ​​pierwsza postać jest, aa następnie cokolwiek (a, ab, abca ...)
  • regex: oznacza zero lub więcej powtórzeń znaku a(a, aa, aaa ...)

Z przyjemnością zgodzę się, że ta rozbieżność znaczeń jest bardzo myląca dla nowych użytkowników.

Globbing jest być może łatwiejszy do uchwycenia dla początkujących, ale jest również mniej wydajnym konstruktem.

fgeorgatos
źródło