Website-Suche

Erweiterter Bash-Regex mit Beispielen


Mit der Leistungsfähigkeit regulärer Ausdrücke können textbasierte Dokumente und Zeichenfolgen analysiert und transformiert werden. Dieser Artikel richtet sich an fortgeschrittene Benutzer, die bereits mit grundlegenden regulären Ausdrücken in Bash vertraut sind. Eine Einführung in reguläre Bash-Ausdrücke finden Sie in unserem Artikel Reguläre Bash-Ausdrücke für Anfänger mit Beispielen. Ein weiterer Artikel, den Sie vielleicht interessant finden könnten, ist Reguläre Ausdrücke in Python.

Sind Sie bereit, loszulegen? Tauchen Sie ein und lernen Sie, Regexps wie ein Profi zu verwenden!

In diesem Tutorial lernen Sie:

  • So vermeiden Sie, dass sich kleine Betriebssystemunterschiede auf Ihre regulären Ausdrücke auswirken
  • So vermeiden Sie die Verwendung von zu generischen Suchmustern für reguläre Ausdrücke wie .*
  • Verwenden oder Nichtverwenden einer erweiterten Syntax für reguläre Ausdrücke
  • Erweiterte Anwendungsbeispiele für komplexe reguläre Ausdrücke in Bash

Erweiterter Bash-Regex mit Beispielen

Verwendete Softwareanforderungen und Konventionen

Beispiel 1: Hinweise zur Verwendung erweiterter regulärer Ausdrücke

In diesem Tutorial verwenden wir sed als unsere Haupt-Engine zur Verarbeitung regulärer Ausdrücke. Alle angegebenen Beispiele können in der Regel direkt auf andere Engines portiert werden, wie z. B. die in grep, awk usw. enthaltenen Engines für reguläre Ausdrücke.

Eine Sache, die Sie bei der Arbeit mit regulären Ausdrücken immer im Hinterkopf behalten sollten, ist, dass einige Regex-Engines (wie die in sed) sowohl reguläre als auch erweiterte Syntax für reguläre Ausdrücke unterstützen. Zum Beispiel ermöglicht Ihnen sed die Verwendung der Option -E (Kurzoption für --regexp-extended), die es Ihnen ermöglicht, erweiterte reguläre Ausdrücke im sed-Skript zu verwenden.

In der Praxis führt dies zu kleinen Unterschieden in der Syntax von regulären Ausdrücken beim Schreiben von Skripten für reguläre Ausdrücke. Schauen wir uns ein Beispiel an:

echo 'sample' | sed 's|[a-e]\+|_|g'
s_mpl_
echo 'sample' | sed 's|[a-e]+|_|g'
sample
echo 'sample+' | sed 's|[a-e]+|_|g'
sampl_
echo 'sample' | sed -E 's|[a-e]+|_|g'
s_mpl_

Wie Sie sehen können, haben wir in unserem ersten Beispiel \+ verwendet, um den a-c-Bereich (der aufgrund des g-Qualifizierers global ersetzt wird) so zu qualifizieren, dass ein oder mehrere Vorkommen erforderlich sind. Beachten Sie, dass die Syntax \+ ist. Als wir jedoch dieses \+ in + änderten, lieferte der Befehl eine völlig andere Ausgabe. Dies liegt daran, dass das + nicht als standardmäßiges Pluszeichen und nicht als Regex-Befehl interpretiert wird.

Dies wurde später durch den dritten Befehl bewiesen, bei dem ein Literal + sowie das davor liegende e durch den regulären Ausdruck [a-e]+ erfasst und in _ umgewandelt wurde.

Wenn wir auf den ersten Befehl zurückblicken, können wir nun sehen, wie \+ als nicht-literaler regulärer Ausdruck + interpretiert wurde, der von sed verarbeitet werden sollte.

Schließlich teilen wir sed im letzten Befehl mit, dass wir speziell die erweiterte Syntax verwenden möchten, indem wir die erweiterte Syntaxoption -E für sed verwenden. Beachten Sie, dass der Begriff "erweitert" uns einen Hinweis darauf gibt, was im Hintergrund passiert; Die Syntax für reguläre Ausdrücke wird erweitert, um verschiedene Regex-Befehle zu aktivieren, wie in diesem Fall +.

Sobald das -E verwendet wird, interpretiert sed das + korrekt, obwohl wir immer noch + und nicht \+ verwenden, als Anweisung für reguläre Ausdrücke.

Wenn Sie viele reguläre Ausdrücke schreiben, treten diese kleinen Unterschiede beim Ausdrücken Ihrer Gedanken in reguläre Ausdrücke in den Hintergrund, und Sie werden dazu neigen, sich an die wichtigsten zu erinnern.

Dies unterstreicht auch die Notwendigkeit, reguläre Ausdrücke immer ausgiebig zu testen, da es eine Vielzahl möglicher Eingaben gibt, auch solche, die Sie nicht erwarten.

Beispiel 2: Heavy-Duty-String-Modifikation

Für dieses und die folgenden Beispiele haben wir eine Textdatei vorbereitet. Wenn Sie mitüben möchten, können Sie die folgenden Befehle verwenden, um diese Datei für sich selbst zu erstellen:

echo 'abcdefghijklmnopqrstuvwxyz ABCDEFG 0123456789' > test1
cat test1
abcdefghijklmnopqrstuvwxyz ABCDEFG 0123456789

Schauen wir uns nun unser erstes Beispiel für Zeichenfolgenmodifikationen an: Wir möchten, dass die zweite Spalte (ABCDEFG) vor der ersten (abcdefghijklmnopqrstuvwxyz) steht.

Als Anfang machen wir diesen fiktiven Versuch:

cat test1
abcdefghijklmnopqrstuvwxyz ABCDEFG 0123456789
cat test1 | sed -E 's|([a-o]+).*([A-Z]+)|\2 \1|'
G abcdefghijklmno 0123456789

Verstehen Sie diesen regulären Ausdruck? Wenn ja, sind Sie bereits ein sehr fortgeschrittener Autor regulärer Ausdrücke, und Sie können sich dafür entscheiden, zu den folgenden Beispielen zu springen und sie zu überfliegen, um zu sehen, ob Sie sie schnell verstehen können oder ein wenig Hilfe benötigen.

Was wir hier tun, ist, unsere test1-Datei cat (anzeigen) und sie mit einem erweiterten regulären Ausdruck (dank der Option -E) mit sed zu analysieren. Wir hätten diesen regulären Ausdruck mit einem nicht erweiterten regulären Ausdruck (in sed) wie folgt schreiben können:

cat test1 | sed 's|\([a-o]\+\).*\([A-Z]\+\)|\2 \1|'
G abcdefghijklmno 0123456789

Das ist genau dasselbe, außer dass wir vor jedem Zeichen (, ) und + ein \-Zeichen hinzugefügt haben, was bedeutet, dass sed sie als Code für reguläre Ausdrücke und nicht als normale Zeichen analysiert werden sollen. Werfen wir nun einen Blick auf den regulären Ausdruck selbst.

Verwenden wir dafür das erweiterte Format für reguläre Ausdrücke, da es visuell einfacher zu analysieren ist.

s|([a-o]+).*([A-Z]+)|\2 \1|

Hier verwenden wir den Befehl sed substitute (s am Anfang des Befehls), gefolgt von einem Abschnitt search (erster |...| Teil) und replace (zweiter |...| Teil).

In der Suche haben wir zwei Auswahlgruppen, die jeweils von ( und ) umgeben und begrenzt sind, nämlich ([a-o]+) und ([A-Z]+). Diese Auswahlgruppen werden in der Reihenfolge, in der sie angegeben sind, beim Durchsuchen der Zeichenfolgen gesucht. Beachten Sie, dass wir zwischen der Auswahlgruppe einen regulären Ausdruck .* haben, der im Grunde genommen ein beliebiges Zeichen bedeutet, 0 oder mehr Mal. Dies entspricht unserem Platz zwischen abcdefghijklmnopqrstuvwxyz und ABCDEFG in der Eingabedatei und möglicherweise mehr.

In unserer ersten Suchgruppe suchen wir nach mindestens einem Vorkommen von a-o, gefolgt von einer beliebigen anderen Anzahl von Vorkommen von a-o, die durch den Qualifizierer + angegeben wird. In der zweiten Suchgruppe suchen wir nach Großbuchstaben zwischen A und Z, und dies wiederum ein oder mehrere Male hintereinander.

Schließlich rufen wir in unserem Replace-Abschnitt des Befehls sed Regular Expression den von diesen Suchgruppen ausgewählten Text zurück/rufen ihn zurück und fügen ihn als Ersetzungszeichenfolgen ein. Beachten Sie, dass die Reihenfolge umgekehrt wird. Geben Sie zuerst den Text aus, der mit der zweiten Auswahlgruppe übereinstimmt (durch die Verwendung von \2, um die zweite Auswahlgruppe anzugeben), dann den Text, der mit der ersten Auswahlgruppe übereinstimmt (\1).

Das mag zwar einfach klingen, aber das vorliegende Ergebnis (G abcdefghijklmno 0123456789) ist möglicherweise nicht sofort klar. Wie haben wir zum Beispiel ABCDEF verloren? Wir haben auch pqrstuvwxyz verloren – haben Sie es bemerkt?

Was passiert ist, ist folgendes; Unsere erste Auswahlgruppe hat den Text abcdefghijklmno aufgenommen. Dann, mit dem .* (beliebiges Zeichen, 0 oder mehrmals) wurden alle Zeichen abgeglichen – und das ist wichtig; bis zum maximalen Umfang – bis wir den nächsten anwendbaren passenden regulären Ausdruck finden, falls vorhanden. Dann, endlich, haben wir jeden Buchstaben aus dem A-Z-Bereich abgeglichen, und zwar noch einmal.

Fangen Sie an zu verstehen, warum wir ABCDEF und pqrstuvwxyz verloren haben? Obwohl es keineswegs selbstverständlich ist, stimmte das .* so lange überein, bis das letzte A-Z übereinstimmte, das G in der ABCDEFG-Zeichenfolge wäre.

Obwohl wir ein oder mehrere (durch die Verwendung von +) Zeichen angegeben haben, die abgeglichen werden sollen, wurde dieser spezielle reguläre Ausdruck von sed von links nach rechts korrekt interpretiert, und sed hörte erst mit dem passenden beliebigen Zeichen (.*) auf, wenn es die Prämisse nicht mehr erfüllen konnte, dass mindestens ein Großbuchstabe von A-Z folgen würde.

Insgesamt wurde pqrstuvwxyz ABCDEF durch .* ersetzt, anstatt nur durch das Leerzeichen, wie man diesen regulären Ausdruck in einer natürlicheren, aber falschen Lesart lesen würde. Und da wir nicht erfassen, was von .* ausgewählt wurde, wurde diese Auswahl einfach aus der Ausgabe entfernt.

Beachten Sie auch, dass alle Teile, die nicht mit dem Suchabschnitt übereinstimmen, einfach in die Ausgabe kopiert werden: sed wird nur auf das reagieren, was der reguläre Ausdruck (oder die Textübereinstimmung) findet.

Beispiel 3: Alles auswählen, was nicht ist

Das vorherige Beispiel führt uns auch zu einer weiteren interessanten Methode, die Sie wahrscheinlich ziemlich oft verwenden werden, wenn Sie regelmäßig reguläre Ausdrücke schreiben, und das ist die Auswahl von Text, indem Sie alles abgleichen, was nicht vorhanden ist. Klingt nach einer lustigen Sache, aber nicht klar, was es bedeutet? Schauen wir uns ein Beispiel an:

cat test1
abcdefghijklmnopqrstuvwxyz ABCDEFG 0123456789
cat test1 | sed -E 's|[^ ]*|_|'
_ ABCDEFG 0123456789

Ein einfacher regulärer Ausdruck, aber ein sehr mächtiger. Anstatt .* in irgendeiner Form oder Weise zu verwenden, haben wir hier [^]* verwendet. Anstatt zu sagen (mit .*) mit jedem Zeichen übereintreffen, 0 oder mehrmals, geben wir jetzt an, dass jedes Zeichen, das kein Leerzeichen ist, 0 oder mehr Mal übereinstimmt.

Das sieht zwar relativ einfach aus, aber Sie werden bald erkennen, wie leistungsfähig es ist, reguläre Ausdrücke auf diese Weise zu schreiben. Denken Sie zum Beispiel an unser letztes Beispiel zurück, in dem wir plötzlich einen großen Teil des Textes auf etwas unerwartete Weise übereinstimmen haben. Dies könnte vermieden werden, indem wir unseren regulären Ausdruck aus dem vorherigen Beispiel wie folgt leicht ändern:

cat test1 | sed -E 's|([a-o]+)[^A]+([A-Z]+)|\2 \1|'
ABCDEFG abcdefghijklmno 0123456789

Noch nicht perfekt, aber schon besser; zumindest konnten wir den ABCDEF-Teil erhalten. Alles, was wir getan haben, war, .* in [^A]+ zu ändern. Mit anderen Worten, suchen Sie weiter nach Zeichen, mindestens einem, außer A. Sobald A gefunden wurde, stoppt das Parsen dieses Teils des regulären Ausdrucks. A selbst wird ebenfalls nicht in das Spiel einbezogen.

Beispiel 4: Rückkehr zu unserer ursprünglichen Anforderung

Können wir es besser machen und tatsächlich die erste und zweite Spalte richtig vertauschen?

Ja, aber nicht, indem der reguläre Ausdruck unverändert bleibt. Schließlich tut es das, worum wir es gebeten haben; Gleichen Sie alle Zeichen von A-O mit der ersten Suchgruppe ab (und geben Sie sie später am Ende der Zeichenfolge aus), und verwerfen Sie dann alle Zeichen, bis sed A erreicht. Wir könnten eine endgültige Lösung des Problems vornehmen – denken Sie daran, dass wir nur das Leerzeichen abgleichen wollten – indem wir das a-o auf a-z erweitern/ändern oder einfach eine weitere Suchgruppe hinzufügen und das Leerzeichen buchstäblich abgleichen:

cat test1 | sed -E 's|([a-o]+)([^ ]+)[ ]([A-Z]+)|\3 \1\2|'
ABCDEFG abcdefghijklmnopqrstuvwxyz 0123456789

Großartig! Aber der reguläre Ausdruck sieht jetzt zu komplex aus. In der ersten Gruppe haben wir ein- oder mehrmals a-o abgeglichen, dann in der zweiten Gruppe ein beliebiges Nicht-Leerzeichen (bis sed ein Leerzeichen oder das Ende der Zeichenfolge findet), dann ein literales Leerzeichen und schließlich ein- oder mehrmals A-Z.

Können wir es vereinfachen? Ja. Und dies sollte zeigen, wie leicht man Skripte für reguläre Ausdrücke übermäßig verkomplizieren kann.

cat test1 | sed -E 's|([^ ]+) ([^ ]+)|\2 \1|'
ABCDEFG abcdefghijklmnopqrstuvwxyz 0123456789
cat test1 | awk '{print $2" "$1" "$3}'
ABCDEFG abcdefghijklmnopqrstuvwxyz 0123456789

Beide Lösungen erfüllen die ursprüngliche Anforderung, indem sie unterschiedliche Tools verwenden, einen stark vereinfachten regulären Ausdruck für den Befehl sed und ohne Fehler, zumindest für die bereitgestellten Eingabezeichenfolgen. Kann das leicht schief gehen?

cat test1
abcdefghijklmnopqrstuvwxyz  ABCDEFG 0123456789
cat test1 | sed -E 's|([^ ]+) ([^ ]+)|\2 \1|'
abcdefghijklmnopqrstuvwxyz  0123456789 ABCDEFG

Ja. Alles, was wir getan haben, war, ein zusätzliches Leerzeichen in der Eingabe hinzuzufügen, und die Verwendung desselben regulären Ausdrucks ist jetzt völlig falsch; Die zweite und dritte Spalte wurden vertauscht, anstatt der ersten zwei. Auch hier wird die Notwendigkeit hervorgehoben, reguläre Ausdrücke eingehend und mit unterschiedlichen Eingaben zu testen. Der Unterschied in der Ausgabe liegt einfach darin, dass das Muster ohne Leerzeichen aufgrund des doppelten Leerzeichens nur mit dem letzten Teil der Eingabezeichenfolge übereinstimmen konnte.

Beispiel 5: ls erwischt?

Manchmal führt eine Einstellung auf Betriebssystemebene, wie z.B. die Verwendung einer Farbausgabe für Verzeichnislisten oder nicht (die standardmäßig gesetzt sein kann!), dazu, dass sich Befehlszeilenskripte unregelmäßig verhalten. Obwohl es keineswegs ein direkter Fehler regulärer Ausdrücke ist, ist es ein Problem, auf das man bei der Verwendung regulärer Ausdrücke leichter stoßen kann. Schauen wir uns ein Beispiel an:

Die LS-Farbausgabe verfälscht das Ergebnis eines Befehls, der reguläre Ausdrücke enthält

ls -d t*
test1  test2
ls -d t*2 | sed 's|2|1|' 
test1
ls -d t*2 | sed 's|2|1|' | xargs ls
ls: cannot access ''

In diesem Beispiel haben wir ein Verzeichnis (test2) und eine Datei (test1), die beide mit dem ursprünglichen Befehl ls -d aufgelistet werden. Dann suchen wir nach allen Dateien mit einem Dateinamensmuster von t*2 und entfernen die 2 aus dem Dateinamen mit sed. Das Ergebnis ist der Texttest. Es sieht so aus, als könnten wir diesen Ausgabetest sofort für einen anderen Befehl verwenden, und wir haben ihn über xargs an den Befehl ls gesendet, in der Erwartung, dass der Befehl ls die Datei test1 auflistet.

Dies geschieht jedoch nicht, und stattdessen erhalten wir eine sehr komplexe Ausgabe zurück, die menschlich zu analysieren ist. Der Grund ist einfach: Das ursprüngliche Verzeichnis wurde in einer dunkelblauen Farbe aufgeführt, und diese Farbe wird als eine Reihe von Farbcodes definiert. Wenn Sie dies zum ersten Mal sehen, ist die Ausgabe schwer zu verstehen. Die Lösung ist jedoch einfach;

ls -d --color=never t*2 | sed 's|2|1|' | xargs ls
test1

Wir haben den Befehl ls dazu gebracht, die Auflistung ohne Farbe auszugeben. Dies behebt das vorliegende Problem vollständig und zeigt uns, wie wir die Notwendigkeit im Hinterkopf behalten können, kleine, aber wichtige betriebssystemspezifische Einstellungen und Fallstricke zu vermeiden, die unsere Arbeit mit regulären Ausdrücken beeinträchtigen können, wenn sie in verschiedenen Umgebungen, auf unterschiedlicher Hardware oder auf verschiedenen Betriebssystemen ausgeführt werden.

Sind Sie bereit, auf eigene Faust weiter zu erkunden? Schauen wir uns einige der gebräuchlichsten regulären Ausdrücke an, die in Bash verfügbar sind:

AusdruckBeschreibung
.
Beliebiges Zeichen, außer Zeilenumbruch
[a-c]
Ein Zeichen des ausgewählten Bereichs, in diesem Fall a,b,c
[A-Z]
Ein Zeichen des ausgewählten Bereichs, in diesem Fall A-Z
[0-9AF-Z]
Ein Zeichen des ausgewählten Bereichs, in diesem Fall 0-9, A und F-Z
[^A-Za-z]
Ein Zeichen außerhalb des ausgewählten Bereichs, in diesem Fall z. B. "1", würde sich qualifizieren
\* or *
Beliebig viele Übereinstimmungen (0 oder mehr). Verwenden Sie *, wenn Sie reguläre Ausdrücke verwenden, bei denen erweiterte Ausdrücke nicht aktiviert sind (siehe erstes Beispiel oben)
\+ or +
1 oder mehr Übereinstimmungen. Idem Kommentar als *
\(\)
Gruppe erfassen. Wenn diese zum ersten Mal verwendet wird, ist die Gruppennummer 1 usw.
^
Anfang der Zeichenfolge
$
Ende der Zeichenkette
\d
Eine Ziffer
\D
Eine nicht ziffernlose Ziffer
\s
Ein Leerzeichen
\S
Ein Nicht-Leerzeichen
a|d
Eines der beiden Zeichen (eine Alternative zur Verwendung von []), 'a' oder 'd'
\
Escapet Sonderzeichen oder gibt an, dass wir einen regulären Ausdruck verwenden möchten, bei dem erweiterte Ausdrücke nicht aktiviert sind (siehe erstes Beispiel oben)
\b
Rücktaste
\n
Zeilenumbruch
\r
Wagenrücklauf-Zeichen
\t
Tabstoppzeichen

Schlussfolgerung

In diesem Tutorial haben wir uns eingehend mit regulären Bash-Ausdrücken befasst. Wir entdeckten die Notwendigkeit, unsere regulären Ausdrücke ausführlich und mit unterschiedlichen Eingaben zu testen. Wir haben auch gesehen, wie kleine Unterschiede im Betriebssystem, wie z. B. die Verwendung von Farbe für ls-Befehle oder nicht, zu sehr unerwarteten Ergebnissen führen können. Wir haben gelernt, wie wichtig es ist, zu generische Suchmuster für reguläre Ausdrücke zu vermeiden und erweiterte reguläre Ausdrücke zu verwenden.

Viel Spaß beim Schreiben fortgeschrittener regulärer Ausdrücke und hinterlassen Sie uns unten einen Kommentar mit Ihren coolsten Beispielen!

Verwandte Artikel: