Geschichte von Bash Globbing

11

Gibt es einen historischen Grund, warum Bash "Globbing" und reguläre Ausdrücke nicht identisch sind? Ich glaube zum Beispiel, dass in Bash [1-2]*alles übereinstimmt, was mit einer 1 oder einer 2 beginnt, gefolgt von etwas anderem, während ein regulärer Ausdruck [1-2]*nur einer Folge von 1s und 2s entspricht. Mein Bash-Scripting und REGEX foo sind beide ziemlich schwach und ich stoße regelmäßig auf Probleme im Zusammenhang mit diesen Unterschieden, die mich neugierig machten, warum sie unterschiedlich sind.

StrongBad
quelle
3
Würdest du tun rm -- ^[^.].*\.txt$statt rm -- *.txt?
Stéphane Chazelas
1
Viele Ihrer Fragen werden in diesem Thread von lwn angesprochen: lwn.net/Articles/96687
slm
Es gibt Befehle, die mit Dateinamen arbeiten und regulären Ausdruck annehmen. Achten Sie beispielsweise beim Suchen find . -regex ".*\.txt$" | xargs rm --oder renameUmbenennen von Dateien (dies gilt sedfür Dateinamen) darauf, dass einige Systeme einen anderen haben rename.
Strg-Alt-Delor
@richard, mein Ziel ^[^.].*\.txt$war es, das Ignorieren von Punktdateien zu berücksichtigen. Beachten Sie, dass das -regexist eine GNU - Erweiterungen, einige Shells wie ksh93 oder zsh kann regexps in ihren Kleckse (zum Beispiel versuchen: inkorporieren ksh93 -c 'echo ~(E:^[^.].*\.txt$)')
Stéphane Chazelas
2
Diese Bash folgt der bestehenden Praxis so sorgfältig und vermeidet unvereinbar unvereinbare Änderungen und Erweiterungen. Dies ist eine der größten Stärken.
Ormaaj

Antworten:

12

bashwurde ursprünglich in den späten 80ern als Teilklon kshmit einigen interaktiven Funktionen von csh / tcsh entworfen.

Die Ursprünge des Globbing müssen in den früheren Muscheln liegen, auf denen es aufbaut.

kshselbst ist eine Erweiterung der Bourne-Shell. Die Bourne-Shell selbst (erstmals 1979 in Unix V7 veröffentlicht) war eine saubere Implementierung von Grund auf neu, weicht jedoch nicht vollständig von der Thompson-Shell (der Shell von V1 -> V6) ab und enthält Funktionen der Mashey-Shell.

Insbesondere wurden Befehlsargumente immer noch durch Leerzeichen getrennt, |war jetzt der neue Pipe-Operator, ^wurde aber weiterhin als Alternative unterstützt (und erklärt auch, warum Sie dies tun [!a-z]und nicht [^a-z]), $1war immer noch das erste Argument für ein Skript und Backslash war immer noch das Escape-Zeichen . So viele der regulären Ausdrucksoperatoren ( ^\|$) haben in der Shell eine eigene Bedeutung.

Die Thompson-Shell stützte sich beim Globbing auf ein externes Dienstprogramm. Wenn shnicht notiert gefunden *, [oder ?s in dem Befehl, würde es den Befehl durchlaufen glob.

rm *.txt

würde am Ende glob laufen als:

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

und glob würde am Ende rmmit der Liste der Dateien ausgeführt, die diesem Muster entsprechen.

grep a.\*b *.txt

würde laufen globals:

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

Das *Obige wurde zitiert, indem das 8. Bit für dieses Zeichen gesetzt wurde, um zu verhindern glob, dass es als Platzhalter behandelt wird. globwürde dann dieses Bit vor dem Aufruf entfernen grep.

Um das Äquivalent mit regulären Ausdrücken zu machen, wäre das gewesen:

regexp rm '\.txt$'

Oder:

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

Punktdateien ausschließen.

Die Notwendigkeit, den Operatoren zu entkommen, da sie als Shell-Sonderzeichen .fungieren , und die Tatsache, dass in Dateinamen häufig ein regulärer Ausdrucksoperator verwendet wird, macht es für Anfänger nicht sehr angemessen, Dateinamen abzugleichen und kompliziert. In den meisten Fällen benötigen Sie lediglich Platzhalter , die entweder ein ( ?) oder eine beliebige Anzahl ( *) von Zeichen ersetzen können .

Jetzt haben verschiedene Shells verschiedene Globbing-Operatoren hinzugefügt. Heutzutage sind die ksh- und zsh-Globs (und in gewissem Maße bash -O extglobeine Teilmenge von ksh-Globs) funktional äquivalent zu regulären Ausdrücken mit einer Syntax, deren Verwendung mit Dateinamen und der aktuellen Shell-Syntax weniger umständlich ist. In zsh(mit Extendedglob-Erweiterung) können Sie beispielsweise Folgendes tun:

echo a#.txt

wenn Sie (unwahrscheinlich) mit Dateinamen übereinstimmen möchten, die aus Sequenzen von agefolgt von bestehen .txt. Einfacher als echo (^a*\.txt$)(hier die Verwendung von geschweiften Klammern, um die Regex-Operatoren von den Shell-Operatoren zu isolieren, die eine Möglichkeit gewesen sein könnten, wie Shells damit umgehen könnten).

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

Für mpg-Dateien (ohne Berücksichtigung der Groß- und Kleinschreibung), deren Basisname foo, bar oder eine Dezimalzahl von 1 bis 20 ist ...

ksh93Jetzt können auch reguläre Ausdrücke (einfach, erweitert, perlartig oder "erweitert") in die Globs integriert werden (obwohl dies ziemlich fehlerhaft ist) und es wird sogar ein Tool zum Konvertieren zwischen glob und regulärem Ausdruck ( printf %R, printf %P) bereitgestellt :

echo ~(Ei:.*\.txt)

zu Spiel (nicht verborgen) txt - Dateien mit E regulären Ausdrücken Xtended, Case- i nsensitively.

Stéphane Chazelas
quelle
Cooles Schreiben! Sie können tatsächlich ~(opt:pat)keine der aktivierten Optionen verwenden. Vielleicht print -r -- ~(Ei).*\.txt$. Das Einfügen des Musters scheint nur nützlich zu sein, um zu vermeiden, dass eine Option für einen Teil eines Musters ein- und ausgeschaltet werden muss. Seltsamerweise können Sie jedoch mehrere Mustersprachen innerhalb eines Globus mischen und anpassen. ~(Ki)*.~(E)txt$ist gleichwertig. (Am Ende wird alles nur in Regex konvertiert und intern an die Regex-Engine von libast übergeben.)
Ormaaj
@ormaaj, ~(Ei:.*\.txt)funktioniert für mich auch mit 15 Jahre alten Versionen wie ksh93 o +.
Stéphane Chazelas
Funktioniert auch mit einer meiner gespeicherten Test-Binärdateien (24.12.2014), aber ich erinnere mich, dass ich damit auf Probleme gestoßen bin. Die Dinge wurden immer zufällig gebrochen und zwischen den einzelnen Versionen wieder repariert, als ksh noch kommerziell entwickelt wurde. Ich erinnere mich, dass der Mustervergleichscode einer der fragilen Bereiche war.
Ormaaj
@ormaaj, ein Unterschied zwischen ~(E)xund ~(E:x)ist, dass Letzteres verankert ist (stimmt xnur überein, während Ersteres mit etwas übereinstimmt x, das etwas enthält ), was möglicherweise die Art von Problem ist, auf die Sie gestoßen sind ( ~(-lr)~(E:x)zum Entfernen der Verankerung ~(E-lr:x)nicht geeignet ). Auf jeden Fall stimme ich zu, dass es selbst in der neuesten Version ziemlich fehlerhaft ist.
Stéphane Chazelas
9

Reguläre Sprachen wurden 1956 von Kleene eingeführt . Das wegweisende Papier hatte nicht die vollständige moderne Notation für reguläre Ausdrücke, aber es führte den „Kleen-Stern“ ein: A*„beliebig viele Wiederholungen von A“. Im nächsten Jahrzehnt entstanden einige mehr oder weniger Standardnotationen, insbesondere .für ein beliebiges Zeichen und um ?zu bedeuten, dass das vorherige Zeichen optional ist.

Die Globbing-Notation von Bash stammt aus dem globBefehl , der bereits 1971 in Unix v1 eingeführt wurde. Zu dieser Zeit wurde das Globbing von einem separaten Programm ausgeführt. es wurde später in die Schale bewegt. Der frühe globBefehl muss ?"ein beliebiges Zeichen" und *"eine beliebige Folge von Zeichen" bedeuten. Ich weiß nicht, warum die Charaktere ausgewählt wurden; ?ist ziemlich intuitiv und wurde *möglicherweise von dem in regulären Ausdrücken inspiriert.

Globbing sollte nicht so allgemein sein wie reguläre Ausdrücke, und reguläre Ausdrücke waren zu dieser Zeit nicht sehr verbreitet, so dass es keinen Aufruf gab, die Konzepte zu vereinheitlichen. Von Anfang an gab es syntaktische Inkompatibilitäten mit ?, .und *was bedeutet , verschiedene Dinge in Dateinamen Muster und in regulären Ausdrücken.

Moderne Muscheln wie Bash erweitern die Glob-Muster, aber es war eine schrittweise Entwicklung, die die Abwärtskompatibilität aufrechterhielt. Ksh88 (die 1988er Version der Korn-Shell ) führte eine erweiterte Syntax für Shell-Muster ein, die nicht die gleiche Syntax wie übliche reguläre Ausdrücke haben konnte, aber stark davon inspiriert war: *(PATTERN)eine beliebige Anzahl von Wiederholungen von PATTERN, oder " oder " @(PATTERN1|PATTERN2)zu bedeuten , etc.PATTERN1PATTERN2

Moderne Versionen von bash (seit 2.02) unterstützen die erweiterten Muster von ksh88, wenn Sie dies shopt -s extglobzuerst tun .

Gilles 'SO - hör auf böse zu sein'
quelle
Hat Bash jemals Extglobs nicht unterstützt? Soweit mir bekannt ist, unterstützen Bash, zsh und {pd, m} ksh seit jeher genau die Globs, die im ksh88-Handbuch dokumentiert sind. Ksh hat bis heute nicht einmal die Option, "erweiterte" Glob-Quantifizierer zu deaktivieren, und ksh93 ist der einzige der Gruppe, der Erweiterungen hat, die über das hinausgehen, was ksh88 hatte.
Ormaaj
2
@ormaaj Ksh88 Extended Globs und die extglobOption wurden irgendwann um 1998 in Bash 2.02 eingeführt. Zsh wurde ksh_globungefähr zur gleichen Zeit in der 3.1-Serie erworben . Zsh hat viele eigene Globbing-Erweiterungen (einige erfordern die extended_globOption).
Gilles 'SO - hör auf böse zu sein'
Aha. Es war also tatsächlich spät genug, um die Notwendigkeit einer Option zu rechtfertigen. (Ich denke, die Standardeinstellung ist heutzutage ziemlich sinnlos, aber interessant.)
Ormaaj
1
@ormaaj, Beachten Sie, dass extglob im bashGegensatz zu kshextglob nicht POSIX-konform macht, da es in Variablen nicht deaktiviert ist. In ksh, var='@(*)'; echo $vardehnt sich im aktuellen Verzeichnis , um alle Dateinamen , die mit Start @(und Ende in )POSIX während erfordert in bash -O extglobes die Dateien auf alle erweitert. (Dennoch kann man Bash-Verhalten hier für sinnvoller halten (und das ksh-Verhalten ist ziemlich schmerzhaft, wenn Sie Muster in Variablen haben möchten)). Diese Glob-Syntax ist deshalb so umständlich (POSIX / Bourne-Kompatibilität). Vergleichen Sie mit zsh Extended Globs.
Stéphane Chazelas
@ StéphaneChazelas Das ist alles wahr, und ich mag, wie klug ksh darüber ist. Es kommt jedoch nur selten zum Spielen, es sei denn, es ist tatsächlich auf POSIX beschränkt. Da fast jede Verwendung für das Teilen von Wörtern durch bessere Funktionen ersetzt wird und das Speichern von Mustern in Variablen ohnehin ein extremes Ärgernis darstellt, da Sie IFS leeren müssen, deaktivieren Sie die Klammererweiterung überall außer bash. Ich denke, es ist immer noch unmöglich, mit gespeicherten Mustern völlig sicher zu sein. Dieses alte Fluchtproblem wurde zum Beispiel nie wirklich gelöst.
Ormaaj
1

Historischer Grund: JA. Referenz:
http://en.wikipedia.org/wiki/Glob_(programming)#Origin

Um die Divergenz zu veranschaulichen, hier ein gutes und einfaches Beispiel: a*

  • Shell Globbing: Bedeutung ist, zuerst Zeichen ist aund dann was auch immer (a, ab, abca ...)
  • Regex: Bedeutung ist, null oder mehr Wiederholungen des Zeichens a(a, aa, aaa ...)

Ich würde gerne zustimmen, dass diese Bedeutungsdiskrepanz für neue Benutzer sehr verwirrend ist.

Globbing ist für Neulinge vielleicht leichter zu verstehen, aber es ist auch ein weniger leistungsfähiges Konstrukt.

fgeorgatos
quelle