103.7 Lektion 1
Zertifikat: |
LPIC-1 |
---|---|
Version: |
5.0 |
Thema: |
103 GNU- und Unix-Befehle |
Lernziel: |
103.7 Textdateien mit regulären Ausdrücken durchsuchen |
Lektion: |
1 von 2 |
Einführung
Algorithmen zur Suche von Zeichenketten sind bei verschiedenen Datenverarbeitungsaufgaben weit verbreitet, so sehr, dass Unix-ähnliche Betriebssysteme ihre eigene, allgegenwärtige Implementierung besitzen: Reguläre Ausdrücke, oft abgekürzt als REs. Reguläre Ausdrücke bestehen aus Zeichenfolgen, die ein generisches Muster bilden, das dazu dient, eine entsprechende Sequenz in einer größeren Zeichenfolge zu lokalisieren und gelegentlich zu modifizieren. Reguläre Ausdrücke erweitern die Möglichkeiten zum:
-
Schreiben von Parsingregeln für Anfragen in HTTP-Servern, nginx im Besonderen.
-
Schreiben von Skripten, die textbasierte Datensätze in ein anderes Format konvertieren.
-
Suchen nach Vorkommnissen von Interesse in Journaleinträgen oder Dokumenten.
-
Filtern von Markupdokumenten, wobei der semantische Inhalt erhalten bleibt.
Der einfachste reguläre Ausdruck enthält mindestens ein Atom. Ein Atom, das so genannt wird, weil es das Grundelement eines regulären Ausdrucks darfstellt, ist nur ein Zeichen, das eine besondere Bedeutung haben kann oder auch nicht. Die meisten gewöhnlichen Zeichen sind eindeutig und behalten ihre wörtliche Bedeutung, während andere eine besondere Bedeutung haben:
.
(Punkt)-
Atom, welches mit jedem beliebigen Zeichen übereinstimmt.
^
(Zirkumflex)-
Atom, welches mit dem Zeileanfang übereinstimmt.
$
(Dollarzeichen)-
Atom, welches mit dem Zeilenende übereinstimmt.
Zum Beispiel kann der reguläre Ausdruck bc
, der sich aus den Literalatomen b
und c
zusammensetzt, in der Zeichenkette abcd
gefunden werden, aber nicht in der Zeichenkette a1cd
. Andererseits kann der reguläre Ausdruck .c
in beiden Zeichenketten abcd
und a1cd
gefunden werden, da der Punkt .
mit einem beliebigen Zeichen übereinstimmt.
Das Zirkumflex- und das Dollarzeichenatom werden verwendet, wenn nur Übereinstimmungen am Anfang oder am Ende der Zeichenkette von Interesse sind. Aus diesem Grund werden diese auch als Anker bezeichnet. Zum Beispiel kann cd
in abcd
gefunden werden, aber ^cd
nicht. In ähnlicher Weise kann ab
in abcd
gefunden werden, aber ab$
nicht. Das Zirkumflex ^
ist ein buchstäbliches Zeichen, außer wenn es am Anfang steht, und $
ist ein buchstäbliches Zeichen, außer wenn es am Ende des regulären Ausdrucks steht.
Klammerausdruck
Es gibt einen weiteren Atomtyp mit dem Namen Klammerausdruck. Obwohl nicht ein einzelnes Zeichen, werden Klammern []
(einschließlich ihres Inhalts) als ein einzelnes Atom betrachtet. Ein Klammerausdruck ist normalerweise nur eine Liste von literarische Zeichen, die von []
umschlossen sind, so dass das Atom mit jedem einzelnen Zeichen aus der Liste übereinstimmt. Zum Beispiel kann der Ausdruck [1b]
in den beiden Zeichenketten abcd
und a1cd
gefunden werden. Um Zeichen anzugeben, denen das Atom nicht entsprechen soll, muss die Liste mit ^
beginnen, wie in [^1b]
. Es ist auch möglich, Zeichenbereiche in Klammerausdrücken anzugeben. Zum Beispiel entspricht [0-9]
den Ziffern 0 bis 9 und [a-z]
jedem Kleinbuchstaben. Die Verwendung von Bereichen ist mit Vorsicht zu genießen, da diese in verschiedenen Gebietsschemata möglicherweise nicht konsistent sind.
Klammerausdruckslisten akzeptieren auch Klassen anstelle von Einzelzeichen und Bereichen. Traditionelle Zeichenklassen lauten:
[:alnum:]
-
Stellt ein alphanumerisches Zeichen dar.
[:alpha:]
-
Stellt ein alphabetisches Zeichen dar.
[:ascii:]
-
Stellt ein Zeichen des ASCII-Zeichensatzes dar.
[:blank:]
-
Stellt ein leeres Zeichen dar, d.h. ein Leerzeichen oder einen Tabulator.
[:cntrl:]
-
Stellt ein Steuerzeichen dar.
[:digit:]
-
Stellt eine Ziffer (0 bis 9) dar.
[:graph:]
-
Stellt jedes druckbare Zeichen außer Leerzeichen dar.
[:lower:]
-
Stellt einen Kleinbuchstaben dar.
[:print:]
-
Stellt jedes druckbare Zeichen einschließlich Leerzeichen dar.
[:punct:]
-
Stellt jedes druckbare Zeichen dar, das weder ein Leerzeichen noch ein alphanumerisches Zeichen ist.
[:space:]
-
Stellt Leerzeichen dar: Leerzeichen, Seitenvorschub (
\f
), Zeilenumbruch (\n
), Wagenrücklauf (\r
), horizontaler Tabulator (\t
) und vertikaler Tabulator (\v
). [:upper:]
-
Stellt einen Großbuchstaben dar.
[:xdigit:]
-
Stellt Hexadezimalziffern (0 bis F) dar.
Zeichenklassen können mit Einzelzeichen und Bereichen kombiniert werden, dürfen aber nicht als Endpunkt eines Bereichs verwendet werden. Außerdem dürfen Zeichenklassen nur in Klammerausdrücken verwendet werden, nicht als unabhängiges Atom außerhalb von Klammern.
Quantifizierer
Die Reichweite eines Atoms, entweder eines einzelnen Zeichenatoms oder eines Klammeratoms, kann mit Hilfe eines Atomquantifizierer skaliert werden. Atomquantifier definieren Atomsequenzen, d.h. Übereinstimmungen treten auf, wenn eine zusammenhängende Wiederholung für das Atom in der Zeichenkette gefunden wird. Die Teilzeichenfolge, die der Übereinstimmung entspricht, wird als Teil bezeichnet. Ungeachtet dessen werden Quantifizierer und andere Merkmale regulärer Ausdrücke unterschiedlich behandelt, je nachdem, welcher Standard verwendet wird.
Nach der Definition von POSIX gibt es zwei Formen von regulären Ausdrücken: “einfache” reguläre Ausdrücke und “erweiterte” reguläre Ausdrücke. Die meisten textbezogenen Programme in jeder herkömmlichen Linux-Distribution unterstützen beide Formen, daher ist es wichtig, ihre Unterschiede zu kennen, um Kompatibilitätsprobleme zu vermeiden und die am besten geeignete Implementierung für die beabsichtigte Aufgabe auszuwählen.
Der *
-Quantifizierer hat die gleiche Funktion sowohl in einfachen als auch in erweiterten REs (Atom kommt garnicht oder öfter vor) und ist ein buchstäbliches Zeichen, wenn er am Anfang des regulären Ausdrucks steht oder wenn ihm ein Backslash \
vorangestellt ist. Mit dem Pluszeichen-Quantifikator +
werden Teile ausgewählt, die ein oder mehrere aufeinanderfolgende Atomübereinstimmungen enthalten. Mit dem Fragezeichen-Quantifizierer ?
wird eine Übereinstimmung erzielt, wenn das entsprechende Atom einmal oder gar nicht vorhanden ist. Wenn ein Backslash \
vorangestellt ist, wird ihre besondere Bedeutung nicht berücksichtigt. Einfache reguläre Ausdrücke unterstützen auch +
und ?
Quantifikatoren, aber diesen muss ein Backslash vorangestellt werden. Im Gegensatz zu erweiterten regulären Ausdrücken sind +
und ?
buchstäbliche Zeichen in einfachen regulären Ausdrücken.
Grenzen
Eine Grenze ist ein Atomquantifikator, der es, wie der Name schon sagt, einem Benutzer erlaubt, präzise Mengengrenzen für ein Atom festzulegen. In erweiterten regulären Ausdrücken kann eine Grenze in drei Formen notiert werden:
{i}
-
Das Atom muss genau
i
mal vorkommen (i
als ganze Zahl). Zum Beispiel stimmt[[:blank:]]{2}
mit genau zwei Leerzeichen überein. {i,}
-
Das Atom muss mindestens
i
mal vorkommen (i
als ganze Zahl). Zum Beispiel stimmt[[:blank:]]{2,}
mit einer beliebigen Folge von zwei oder mehr Leerzeichen überein. {i,j}
-
Das Atom muss mindestens
i
mal und höchstensj
mal vorkommen (i
undj
als ganze Zahlen,j
größer alsi
). Beispielsweise entsprichtxyz{2,4}
der Zeichenkettexy
, gefolgt von zwei- bis viermal dem Zeichenz
.
In jedem Fall wird, wenn eine Teilzeichenkette mit einem regulären Ausdruck übereinstimmt und eine längere Teilzeichenkette, die an der gleichen Stelle beginnt, ebenfalls übereinstimmt, die längere Teilzeichenkette berücksichtigt.
Einfache reguläre Ausdrücke unterstützen auch Grenzen, aber den Begrenzungen muss ein \
vorangestellt werden: \{
und \}
. {
und }
werden von sich aus als buchstäbliche Zeichen interpretiert. Ein \{
, gefolgt von einem anderen Zeichen als einer Ziffer, ist ein buchstäbliches Zeichen und nicht der Anfang einer Begrenzung.
Zweige und Rückverweise
Einfache reguläre Ausdrücke unterscheiden sich von den erweiterten regulären Ausdrücken auch in einem weiteren wichtigen Aspekt: Ein erweiterter regulärer Ausdruck kann in Zweige (branch) unterteilt werden, von denen jeder ein unabhängiger regulärer Ausdruck ist. Zweige werden durch |
getrennt, und der kombinierte reguläre Ausdruck wird auf alles passen, was einem der Zweige entspricht. Zum Beispiel wird he|him
übereinstimmen, wenn entweder die Teilzeichenkette he
oder him
in der untersuchten Zeichenkette enthalten ist. Einfache reguläre Ausdrücke interpretieren |
als ein buchstäbliches Zeichen. Die meisten Programme, die einfache reguläre Ausdrücke unterstützen, erlauben jedoch Verzweigungen mittels \|
.
Ein erweiterter regulärer Ausdruck, der in ()
eingeschlossen ist, kann in einer Rückreferenz verwendet werden. Zum Beispiel wird ([[:digit:]])\1
mit jedem regulären Ausdruck übereinstimmen, der sich mindestens einmal wiederholt, weil das \1
im Ausdruck den Rückverweis auf einen Teil darstellt, das mit dem ersten eingeklammerten Teilausdruck übereinstimmt. Wenn mehr als ein eingeklammerter Teilausdruck im regulären Ausdruck existiert, kann auf diese mit \2
, \3
usw. verwiesen werden.
Bei einfachen REs müssen die Teilausdrücke von \(
und \)
umschlossen werden, wobei (
und )
selbst nur gewöhnliche Zeichen darstellen. Der Indikator für Rückverweies wird wie in erweiterten regulären Ausdrücken verwendet.
Suchen mit regulären Ausdrücken
Der unmittelbare Nutzen, den reguläre Ausdrücke bieten, besteht darin, die Suche auf Dateisystemen und in Textdokumenten zu verbessern. Die Option -regex
des Befehls find
erlaubt es, jeden Pfad in einer Verzeichnishierarchie gegen einen regulären Ausdruck zu testen. Zum Beispiel,
$ find $HOME -regex '.*/\..*' -size +100M
sucht nach Dateien, die größer als 100 Megabyte (100 Einheiten von 1048576 Bytes) sind, aber nur in Pfaden innerhalb des Homeverzeichnisses des Benutzers, die eine Übereinstimmung mit .*/\..*
enthalten, d.h. ein /.
umgeben von einer beliebigen anderen Anzahl von Zeichen. Mit anderen Worten, es werden nur versteckte Dateien oder Dateien innerhalb versteckter Verzeichnisse aufgelistet, unabhängig von der Position von /.
im entsprechenden Pfad. Für reguläre Ausdrücke ohne Berücksichtigung der Groß-/Kleinschreibung sollte stattdessen die Option -iregex
verwendet werden:
$ find /usr/share/fonts -regextype posix-extended -iregex '.*(dejavu|liberation).*sans.*(italic|oblique).*' /usr/share/fonts/dejavu/DejaVuSansCondensed-BoldOblique.ttf /usr/share/fonts/dejavu/DejaVuSansCondensed-Oblique.ttf /usr/share/fonts/dejavu/DejaVuSans-BoldOblique.ttf /usr/share/fonts/dejavu/DejaVuSans-Oblique.ttf /usr/share/fonts/dejavu/DejaVuSansMono-BoldOblique.ttf /usr/share/fonts/dejavu/DejaVuSansMono-Oblique.ttf /usr/share/fonts/liberation/LiberationSans-BoldItalic.ttf /usr/share/fonts/liberation/LiberationSans-Italic.ttf
In diesem Beispiel enthält der reguläre Ausdruck Zweige (im erweiterten Stil notiert), um nur bestimmte Schriftartendateien unter der Verzeichnishierarchie /usr/share/fonts
aufzulisten. Erweiterte reguläre Ausdrücke werden standardmäßig nicht unterstützt, aber find
erlaubt es, diese mit -regextype posix-extended
oder -regextype egrep
zu aktivieren. Der RE-Standard für find
ist findutils-default, welcher nahezu ein einfacher Klon eines regulären Ausdrucks darstellt.
Oft ist es notwendig, die Ausgabe eines Programms an den Befehl less
zu übergeben, wenn diese nicht auf den Bildschirm passt. Der Befehl less
teilt seine Eingaben in Seiten auf, eine Bildschirmseite nach der anderen, so dass der Benutzer leicht durch den Text auf und ab navigieren kann. Darüber hinaus erlaubt less
dem Benutzer auch, auf regulären Ausdrücken basierende Suchvorgänge durchzuführen. Diese Funktion ist besonders wichtig, weil less
der Standardpager ist und für viele alltägliche Aufgaben verwendet wird, wie z.B. für die Inspektion von Journaleinträgen oder die Konsultation von Handbuchseiten. Beispielsweise kann beim Lesen einer Handbuchseite durch Drücken der Taste / die Suchaufforderung geöffnet werden. Dies ist ein typisches Szenario, in dem reguläre Ausdrücke nützlich sind, da die Befehlsoptionen im allgemeinen Handbuchseitenlayout sich über mehrere Seiten erstrecken. Dieselbe Option könnte jedoch mehrmals im Text vorkommen, so dass eine wörtliche Suche nicht zum Erfolg führt. Unabhängig davon springt die Eingabe von ^[[:blank:]]*-o
— oder einfacher: ^ *-o
— in der Suchaufforderung nach Drücken der Eingabetaste sofort zur Option des Abschnitts -o
(falls vorhanden), so dass man eine Optionsbeschreibung schneller konsultieren kann.
Geführte Übungen
-
Welcher erweiterte reguläre Ausdruck würde auf eine beliebige E-Mail-Adresse wie
info@example.org
passen? -
Welcher erweiterte reguläre Ausdruck würde auschließlich auf eine beliebige IPv4-Adresse in Punktnotation passen, wie
192.168.15.1
? -
Wie kann der Befehl
grep
verwendet werden, um den Inhalt der Datei/etc/services
aufzulisten, wobei alle Kommentare (Zeilen, die mit#
beginnen) verworfen werden? -
Die Datei
domains.txt
enthält eine Liste von Domainnamen, einen pro Zeile. Wie würde der Befehlegrep
benutzt, um nur.org
- oder.com
-Domains aufzulisten?
Offene Übungen
-
Wie würde der Befehl
find
vom aktuellen Verzeichnis aus einen erweiterten regulären Ausdruck verwenden, um nach allen Dateien zu suchen, die kein Standard-Dateisuffix enthalten (z.B. Dateinamen, die nicht auf.txt
oder.c
enden)? -
Der Befehl
less
ist der Standardpager für die Anzeige langer Textdateien in einer Shellumgebung. Durch Eingabe von/
kann ein regulärer Ausdruck in die Suchabfrage eingegeben werden, um zur ersten entsprechenden Übereinstimmung zu springen. Um an der aktuellen Dokumentposition zu bleiben und nur die Übereinstimmungen hervorzuheben, welche Tastenkombination sollte bei der Suchabfrage eingegeben werden? -
Wie wäre es mittels
less
möglich, die Ausgabe so zu filtern, dass nur Zeilen angezeigt werden, die einem regulären Ausdruck entsprechen?
Zusammenfassung
Diese Lektion behandelt die allgemeine Linuxunterstützung für reguläre Ausdrücke, einen weit verbreiteten Standard, dessen Mustervergleichsfunktionen von den meisten textbezogenen Programmen unterstützt werden. Die Lektion behandelt die folgenden Schritte:
-
Was ein regulärer Ausdruck ist.
-
Die Hauptkomponenten eines regulären Ausdrucks.
-
Die Unterschiede zwischen einfachen und erweiterten regulären Ausdrücken.
-
Wie man einfache Text- und Dateisuchen mit regulären Ausdrücken durchführt.
Lösungen zu den geführten Übungen
-
Welcher erweiterte reguläre Ausdruck würde auf eine beliebige E-Mail-Adresse wie
info@example.org
passen?egrep "\S+@\S+\.\S+"
-
Welcher erweiterte reguläre Ausdruck würde auschließlich auf eine beliebige IPv4-Adresse in Punktnotation passen, wie
192.168.15.1
?egrep "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}"
-
Wie kann der Befehl
grep
verwendet werden, um den Inhalt der Datei/etc/services
aufzulisten, wobei alle Kommentare (Zeilen, die mit#
beginnen) verworfen werden?grep -v ^# /etc/services
-
Die Datei
domains.txt
enthält eine Liste von Domainnamen, einen pro Zeile. Wie würde der Befehlegrep
benutzt, um nur.org
- oder.com
-Domains aufzulisten?egrep ".org$|.com$" domains.txt
Lösungen zu den offenen Übungen
-
Wie würde der Befehl
find
vom aktuellen Verzeichnis aus einen erweiterten regulären Ausdruck verwenden, um nach allen Dateien zu suchen, die kein Standard-Dateisuffix enthalten (z.B. Dateinamen, die nicht auf.txt
oder.c
enden)?find . -type f -regextype egrep -not -regex '.*\.[[:alnum:]]{1,}$'
-
Der Befehl
less
ist der Standardpager für die Anzeige langer Textdateien in einer Shellumgebung. Durch Eingabe von/
kann ein regulärer Ausdruck in die Suchabfrage eingegeben werden, um zur ersten entsprechenden Übereinstimmung zu springen. Um an der aktuellen Dokumentposition zu bleiben und nur die Übereinstimmungen hervorzuheben, welche Tastenkombination sollte bei der Suchabfrage eingegeben werden?Durch Drücken von Strg+K, bevor der Suchausdruck eingegeben wird.
-
Wie wäre es mittels
less
möglich, die Ausgabe so zu filtern, dass nur Zeilen angezeigt werden, die einem regulären Ausdruck entsprechen?Durch Drücken von & und Eingabe des Suchbegriffs.