GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Regex und grep:Datenfluss und Bausteine

In Einführung in reguläre Ausdrücke , habe ich behandelt, was sie sind und warum sie nützlich sind. Schauen wir uns nun genauer an, wie sie erstellt werden. Weil GNU grep eines der Tools ist, die ich am häufigsten verwende (das eine mehr oder weniger standardisierte Implementierung regulärer Ausdrücke bereitstellt), werde ich diesen Satz von Ausdrücken als Grundlage für diesen Artikel verwenden. Wir werden uns dann sed ansehen (ein weiteres Tool, das reguläre Ausdrücke verwendet) in einem späteren Artikel.

Alle Implementierungen regulärer Ausdrücke sind zeilenbasiert. Ein durch eine Kombination aus einem oder mehreren Ausdrücken erstelltes Muster wird mit jeder Zeile eines Datenstroms verglichen. Wenn eine Übereinstimmung gefunden wird, wird in dieser Zeile eine Aktion durchgeführt, wie es das verwendete Tool vorschreibt.

Zum Beispiel, wenn eine Musterübereinstimmung mit grep auftritt , besteht die übliche Aktion darin, diese Zeile an STDOUT zu übergeben und Zeilen zu verwerfen, die nicht mit dem Muster übereinstimmen. Wie wir in Erste Schritte mit regulären Ausdrücken:Ein Beispiel gesehen haben , das -v Option kehrt diese Aktionen um, sodass die Zeilen mit Übereinstimmungen verworfen werden.

Jede Zeile des Datenstroms wird für sich ausgewertet. Stellen Sie sich jede Datenstromlinie als einen Datensatz vor, bei dem die Tools, die reguläre Ausdrücke verwenden, einen Datensatz nach dem anderen verarbeiten. Wenn eine Übereinstimmung gefunden wird, wird eine vom verwendeten Tool definierte Aktion in der Zeile ausgeführt, die die übereinstimmende Zeichenfolge enthält.

Regex-Bausteine

Die folgende Tabelle enthält eine Liste der grundlegenden Bausteinausdrücke und Metazeichen, die von GNU grep implementiert werden Befehl (und die meisten anderen Regex-Implementierungen) und ihre Beschreibungen. Bei Verwendung in einem Muster entspricht jeder dieser Ausdrücke oder Metazeichen einem einzelnen Zeichen im zu analysierenden Datenstrom:

Ausdruck Beschreibung

Alphanumerische Zeichen

Literale

A–Z,a–z,0–9

Alle alphanumerischen und einige Satzzeichen werden als Literale betrachtet. Also der Buchstabe a in einer Regex stimmt immer mit dem Buchstaben "a" im zu analysierenden Datenstrom überein. Es gibt keine Mehrdeutigkeit für diese Zeichen. Jedes Literalzeichen entspricht genau einem Zeichen.
. (Punkt) Das Punkt-Metazeichen (.) ist die grundlegendste Ausdrucksform. Es stimmt mit jedem einzelnen Zeichen an der Position überein, an der es in einem Muster angetroffen wird. Also das Muster b.g würde mit „big“, „bigger“, „bag“, „baguette“ und „moor“ übereinstimmen, aber nicht mit „dog“, „blog“, „hug“, „lag“, „gag“, „leg“ usw .

Klammerausdruck

[Liste der Zeichen]

GNU grep nennt dies einen Klammerausdruck, und es ist dasselbe wie ein Satz für die Bash-Shell. Die Klammern schließen eine Liste von Zeichen ein, die für eine einzelne Zeichenstelle im Muster übereinstimmen sollen. [abcdABCD] stimmt mit den Buchstaben „a“, „b“, „c“ oder „d“ in Groß- oder Kleinschreibung überein. [a-dA-D] gibt einen Bereich von Zeichen an, der dieselbe Übereinstimmung erzeugt. [a-zA-Z] entspricht dem Alphabet in Groß- und Kleinschreibung.

[:Klassenname:]

Zeichenklassen

Dies ist ein POSIX-Versuch einer Regex-Standardisierung. Die Klassennamen sollten offensichtlich sein. Beispiel:[:alnum:] Klasse stimmt mit allen alphanumerischen Zeichen überein. Andere Klassen sind [:digit :] was mit einer beliebigen Ziffer 0-9 übereinstimmt, [:alpha:] ,[:space:] , und so weiter. Beachten Sie, dass es aufgrund von Unterschieden in der Sortierreihenfolge in verschiedenen Gebietsschemas zu Problemen kommen kann. Lesen Sie das grep Manpage für Details.

^ und $

Anker

Diese beiden Metazeichen stimmen mit dem Anfang bzw. dem Ende einer Zeile überein. Sie sollen den Rest des Musters entweder am Anfang oder am Ende einer Linie verankern. Der Ausdruck ^b.g würde nur mit „big“, „bigger“, „bag“ usw. übereinstimmen, wie oben gezeigt, wenn sie am Anfang der zu analysierenden Zeile stehen. Das Muster b.g$ würde "big" oder "bag" nur dann finden, wenn sie am Ende der Zeile stehen, aber nicht "bigger."

Lassen Sie uns diese Bausteine ​​untersuchen, bevor wir mit einigen der Modifikatoren fortfahren. Die Textdatei, die wir für Experiment 3 verwenden, stammt aus einem Laborprojekt, das ich für eine alte Linux-Klasse erstellt habe, die ich früher unterrichtet habe. Es war ursprünglich in einer LibreOffice Writer-ODT-Datei, aber ich habe es in einer ASCII-Textdatei gespeichert. Der größte Teil der Formatierung von Dingen wie Tabellen wurde entfernt, aber das Ergebnis ist eine lange ASCII-Textdatei, die wir für diese Reihe von Experimenten verwenden können.

Beispiel:TOC-Einträge

Schauen wir uns ein Beispiel an, um zu untersuchen, was wir gerade gelernt haben. Erstellen Sie zuerst den ~/testing Verzeichnis Ihr PWD (erstellen Sie es, falls Sie es nicht bereits im vorherigen Artikel dieser Serie getan haben), und laden Sie dann die Beispieldatei von GitHub herunter.

[student@studentvm1 testing]$  wget https://raw.githubusercontent.com/opensourceway/reg-ex-examples/master/Experiment_6-3.txt

Verwenden Sie zu Beginn das less Befehl zum Ansehen und Untersuchen der Experiment_6-3.txt Datei für ein paar Minuten, um sich ein Bild von ihrem Inhalt zu machen.

Lassen Sie uns nun ein einfaches grep verwenden Ausdrücke, um Zeilen aus dem Eingabedatenstrom zu extrahieren. Das Inhaltsverzeichnis (TOC) enthält eine Liste der Projekte und ihre jeweiligen Seitenzahlen im PDF-Dokument. Lassen Sie uns das Inhaltsverzeichnis extrahieren, beginnend mit Zeilen, die mit zwei Ziffern enden:

[student@studentvm1 testing]$  grep [0-9][0-9]$ Experiment_6-3.txt

Dieser Befehl ist nicht wirklich das, was wir wollen. Es zeigt alle Zeilen an, die mit zwei Ziffern enden, und vermisst TOC-Einträge mit nur einer Ziffer. Wie man mit einem Ausdruck für eine oder mehrere Ziffern umgeht, sehen wir uns in einem späteren Experiment an. Betrachten der gesamten Datei in less , könnten wir so etwas tun.

[student@studentvm1 testing]$ grep "^Lab Project" Experiment_6-3.txt | grep "[0-9]$"

Dieser Befehl ist viel näher an dem, was wir wollen, aber er ist nicht ganz da. Wir erhalten später im Dokument einige Zeilen, die ebenfalls mit diesen Ausdrücken übereinstimmen. Wenn Sie die zusätzlichen Zeilen studieren und sich diese im vollständigen Dokument ansehen, können Sie sehen, warum sie übereinstimmen, obwohl sie nicht Teil des Inhaltsverzeichnisses sind.

Dieser Befehl übersieht auch TOC-Einträge, die nicht mit „Lab Project“ beginnen. Manchmal ist dieses Ergebnis das Beste, was Sie erreichen können, und es gibt einen besseren Einblick in das Inhaltsverzeichnis als zuvor. Wir werden uns ansehen, wie man diese beiden grep kombiniert Instanzen in einem späteren Experiment in eine einzige.

Lassen Sie uns diesen Befehl nun ein wenig modifizieren und den POSIX-Ausdruck verwenden. Beachten Sie die doppelten eckigen Klammern ([[]] ) drumherum:

[student@studentvm1 testing]$ grep "^Lab Project" Experiment_6-3.txt | grep "[[:digit:]]$"

Einzelne geschweifte Klammern erzeugen eine Fehlermeldung.

Dieser Befehl liefert dieselben Ergebnisse wie der vorherige Versuch.

Beispiel:systemd

Lassen Sie uns in derselben Datei nach etwas anderem suchen:

[student@studentvm1 testing]$ grep systemd Experiment_6-3.txt

Dieser Befehl listet alle Vorkommen von „systemd“ in der Datei auf. Versuchen Sie es mit -i Option, um sicherzustellen, dass Sie alle Instanzen erhalten, einschließlich derjenigen, die mit Großbuchstaben beginnen (die offizielle Form von „systemd“ ist ausschließlich Kleinbuchstaben). Oder Sie könnten den wörtlichen Ausdruck in Systemd ändern .

Zählen Sie die Anzahl der Zeilen, die den String systemd enthalten . Ich verwende immer -i um sicherzustellen, dass alle Instanzen des Suchausdrucks unabhängig von der Groß-/Kleinschreibung gefunden werden:

[student@studentvm1 testing]$ grep -i systemd Experiment_6-3.txt | wc
20      478     3098

Wie Sie sehen können, habe ich 20 Zeilen und Sie sollten dieselbe Nummer haben.

Beispiel:Metazeichen

Hier ist ein Beispiel für den Abgleich eines Metazeichens:die linke Klammer ([ ). Versuchen wir es zunächst, ohne etwas Besonderes zu tun:

[student@studentvm1 testing]$  **grep -i "[" Experiment_6-3.txt**
grep: Invalid regular expression

Dieser Fehler tritt auf, weil [ wird als Metazeichen interpretiert. Wir müssen fliehen dieses Zeichen mit einem Backslash (\ ), sodass es als Literalzeichen und nicht als Metazeichen interpretiert wird:

[student@studentvm1 testing]$ grep -i "\[" Experiment_6-3.txt

Die meisten Metazeichen verlieren ihre besondere Bedeutung, wenn sie innerhalb von Klammerausdrücken verwendet werden:

  • Um einen wörtlichen ] , platzieren Sie es an erster Stelle in der Liste.
  • Um einen wörtlichen ^ einzuschließen , platzieren Sie es irgendwo, aber zuerst.
  • Um einen wörtlichen [ einzufügen , platzieren Sie es zuletzt.

Wiederholung

Reguläre Ausdrücke können mithilfe von Operatoren geändert werden, mit denen Sie keine, eine oder mehrere Wiederholungen eines Zeichens oder Ausdrucks angeben können. Diese Wiederholungsoperatoren werden unmittelbar nach dem im Muster verwendeten Literalzeichen oder Metazeichen platziert:

Operator Beschreibung
?

In regulären Ausdrücken ist das ? bedeutet null oder höchstens ein Vorkommen des vorangehenden Zeichens. Also zum Beispiel drives? stimmt mit "drive" und "drives" überein, aber nicht mit "driver". Dieses Ergebnis unterscheidet sich etwas vom Verhalten von ? in einem Globus.

* Das Zeichen vor dem * wird null oder mehrmals ohne Limit abgeglichen. In diesem Beispiel drives* stimmt mit „drive“, „drives“ und „drivesss“, aber nicht mit „driver“ überein. Auch dies unterscheidet sich ein wenig vom Verhalten von * in einem Globus.
+ Das Zeichen vor dem + wird ein- oder mehrmals abgeglichen. Das Zeichen muss mindestens einmal in der Zeile vorhanden sein, damit eine Übereinstimmung auftritt. Beispielsweise drives+ stimmt mit „drives“ und „drivesss“, aber nicht mit „drive“ oder „driver“ überein.
{n} Dieser Operator stimmt genau n Mal mit dem vorhergehenden Zeichen überein. Der Ausdruck drives{2} stimmt mit "drivess", aber nicht mit "drive", "drives", "drivesss" oder einer beliebigen Anzahl nachgestellter "s"-Zeichen überein. Da "drivesssss" jedoch die Zeichenfolge drivess enthält , findet eine Übereinstimmung mit dieser Zeichenfolge statt, sodass die Zeile eine Übereinstimmung mit grep wäre .
{n,} Dieser Operator stimmt n oder öfter mit dem vorhergehenden Zeichen überein. Der Ausdruck drives{2,} stimmt mit „drivess“, aber nicht mit „drive“, „drives“, „drivess “, „drives“ oder einer beliebigen Anzahl nachgestellter „s“-Zeichen überein. Denn "drivesssss" enthält den String drivess , findet eine Übereinstimmung statt.
{,m} Dieser Operator passt das vorhergehende Zeichen nicht mehr als m mal an. Der Ausdruck drives{,2} stimmt mit "drive", "drives" und "drivess" überein, aber nicht mit "drivesss" oder einer beliebigen Anzahl nachgestellter "s"-Zeichen. Noch einmal, weil "drivesssss" den String drivess enthält , findet eine Übereinstimmung statt.
{n,m} Dieser Operator stimmt mindestens n-mal, aber nicht mehr als m-mal mit dem vorhergehenden Zeichen überein. Der Ausdruck drives{1,3} stimmt mit "drives", "drivess" und "drivesss" überein, aber nicht mit "drivessss" oder einer beliebigen Anzahl nachgestellter "s"-Zeichen. Da "drivesssss" eine übereinstimmende Zeichenfolge enthält, kommt es erneut zu einer Übereinstimmung.

Führen Sie als Beispiel jeden der folgenden Befehle aus und untersuchen Sie die Ergebnisse sorgfältig, damit Sie verstehen, was passiert:

[student@studentvm1 testing]$  **grep -E files? Experiment_6-3.txt**
[student@studentvm1 testing]$  **grep -Ei "drives*" Experiment_6-3.txt**
[student@studentvm1 testing]$  **grep -Ei "drives+" Experiment_6-3.txt**
[student@studentvm1 testing]$  **grep -Ei "drives{2}" Experiment_6-3.txt**
[student@studentvm1 testing]$  **grep -Ei "drives{2,}" Experiment_6-3.txt**
[student@studentvm1 testing]$  **grep -Ei "drives{,2}" Experiment_6-3.txt**
[student@studentvm1 testing]$  **grep -Ei "drives{2,3}" Experiment_6-3.txt**

Experimentieren Sie unbedingt mit diesen Modifikatoren an anderem Text in der Beispieldatei.

Metazeichen-Modifikatoren

Es gibt noch einige interessante und wichtige Modifikatoren, die wir untersuchen müssen:

Modifizierer Beschreibung
< Dieser spezielle Ausdruck entspricht der leeren Zeichenkette am Anfang eines Wortes. Der Ausdruck <fun würde "fun" und "Function" entsprechen, aber nicht "refund".
> Dieser spezielle Ausdruck entspricht dem normalen Leerzeichen oder der leeren Zeichenfolge (" ") am Ende eines Wortes sowie der Interpunktion, die normalerweise in der Zeichenfolge mit einem Zeichen am Ende eines Wortes erscheint. Also environment> stimmt mit "environment", "environment" und "environment" überein, aber nicht mit "environments" oder "environmental".
^ In einem Zeichenklassenausdruck negiert dieser Operator die Zeichenliste. Während also die Klasse [a-c] entspricht "a", "b" oder "c" an dieser Position des Musters, der Klasse [^a-c] stimmt mit allem außer "a", "b" oder "c" überein.
| Bei Verwendung in einem regulären Ausdruck ist der | Metazeichen ist ein logischer "oder"-Operator. Es wird offiziell als Infix bezeichnet oder Wechsel Operator. Wir sind diesem bereits in Erste Schritte mit regulären Ausdrücken:Ein Beispiel begegnet , wo wir gesehen haben, dass die Regex "Team|^\s*$" bedeutet "eine Zeile mit 'Team' oder (| ) eine leere Zeile mit null, einem oder mehreren Leerzeichen wie Leerzeichen, Tabulatoren und anderen nicht druckbaren Zeichen."
( and ) Die Klammern ( and ) ermöglichen es uns, eine bestimmte Abfolge von Mustervergleichen sicherzustellen, wie sie für logische Vergleiche in einer Programmiersprache verwendet werden könnten.

Wir haben jetzt eine Möglichkeit, Wortgrenzen mit \< festzulegen und \> Metazeichen. Das bedeutet, dass wir jetzt noch deutlicher mit unseren Mustern umgehen können. Wir können Logik auch in komplexeren Mustern verwenden.

Beginnen Sie beispielsweise mit ein paar einfachen Mustern. Dieser erste wählt alle Instanzen von drives aus aber nicht drive , drivess , oder zusätzliche nachgestellte "s"-Zeichen:

 [student@studentvm1 testing]$  **grep -Ei "\<drives\>" Experiment_6-3.txt**

Lassen Sie uns nun ein Suchmuster erstellen, um Verweise auf tar zu finden (der Bandarchivierungsbefehl) und zugehörige Referenzen. Die ersten beiden Iterationen zeigen mehr als nur tar -bezogene Zeilen:

[student@studentvm1 testing]$ grep -Ei "tar" Experiment_6-3.txt
[student@studentvm1 testing]$ grep -Ei "\<tar" Experiment_6-3.txt
[student@studentvm1 testing]$  grep -Ein "\<tar\>" Experiment_6-3.txt

Das -n Option im letzten Befehl oben zeigt die Zeilennummern für jede Zeile an, in der eine Übereinstimmung aufgetreten ist. Diese Option kann beim Auffinden bestimmter Instanzen des Suchmusters helfen.

Tipp: Übereinstimmende Datenzeilen können über einen einzelnen Bildschirm hinausgehen, insbesondere wenn eine große Datei durchsucht wird. Sie können den resultierenden Datenstrom durch das Less-Dienstprogramm leiten und dann die Less-Suchfunktion verwenden, die auch reguläre Ausdrücke implementiert, um das Vorkommen von Übereinstimmungen mit dem Suchmuster hervorzuheben. Das Suchargument in less lautet:\<tar\> .

Dieses nächste Muster sucht in unserem Testdokument nach „Shell-Skript“, „Shell-Programm“, „Shell-Variable“, „Shell-Umgebung“ oder „Shell-Eingabeaufforderung“. Die Klammern ändern die logische Reihenfolge, in der die Mustervergleiche aufgelöst werden:

[student@studentvm1 testing]$ grep -Eni "\<shell (script|program|variable|environment|prompt)" Experiment_6-3.txt

Hinweis: Dieser Artikel ist eine leicht modifizierte Version von Kapitel 6 aus Band 2 meines Linux-Buchs „Using and Administering Linux:Zero to SysAdmin“, das Ende 2019 bei Apress erscheinen soll.

Entfernen Sie die Klammern aus dem vorherigen Befehl und führen Sie ihn erneut aus, um den Unterschied zu sehen.

Abschluss

Obwohl wir jetzt die Grundbausteine ​​regulärer Ausdrücke in grep untersucht haben , gibt es unendlich viele Möglichkeiten, wie sie kombiniert werden können, um komplexe und dennoch elegante Suchmuster zu erstellen. Jedoch grep ist ein Suchwerkzeug und bietet keine direkte Möglichkeit, eine Textzeile im Datenstrom zu bearbeiten oder zu ändern, wenn eine Übereinstimmung besteht. Dazu benötigen wir ein Tool wie sed , die ich in meinem nächsten Artikel behandeln werde.


Linux
  1. Befehl Grep und Locate verwenden?

  2. Cut / Grep und Df -h?

  3. Grep und Schwanz -f?

  4. Die Herausforderung und das Versprechen von Big Data

  5. Grundlagen zu Vhosts und Serverblöcken

Grep Regex:Ein vollständiger Leitfaden

Reguläre Ausdrücke in Grep (Regex)

KDE Connect wird immer besser

So verbinden und teilen Sie Daten zwischen zwei Linux-Systemen

Apache Cassandra:Funktionen und Installation

10 praktische Beispiele für Regex mit grep