Grep: Das Sternchen (*) funktioniert nicht immer

Grep: Das Sternchen (*) funktioniert nicht immer

Wenn ich ein Dokument greppe, das Folgendes enthält:

ThisExampleString

...für den Ausdruck This*Stringoder *Stringwird nichts zurückgegeben. Gibt jedoch This*wie erwartet die obige Zeile zurück.

Es spielt keine Rolle, ob der Ausdruck in Anführungszeichen eingeschlossen ist.

Ich dachte, das Sternchen weist auf eine beliebige Anzahl unbekannter Zeichen hin? Warum funktioniert es nur, wenn es am Anfang des Ausdrucks steht? Wenn dies beabsichtigtes Verhalten ist, was verwende ich anstelle der Ausdrücke This*Stringund *String?

Antwort1

Ein Asterisk inReguläre Ausdrückebedeutet „passt 0 oder mehr Mal zum vorhergehenden Element“.

In Ihrem speziellen Fall mit grep 'This*String' file.txtversuchen Sie zu sagen: „Hey, grep, finde für mich das Wort Thi, gefolgt von einem Kleinbuchstaben snull- oder mehrmals, gefolgt vom Wort String". Der Kleinbuchstabe sist in nirgends zu finden Example, daher ignoriert grep ThisExampleString.

Im Fall von grep '*String' file.txtsagen Sie: „grep, finde den leeren String – buchstäblich nichts – vor dem Wort String„. Natürlich ist das nicht die Art, wie ThisExampleStringes gelesen werden soll. (Es gibtandere mögliche Bedeutungen– Sie können dies mit und ohne Flagge versuchen -E– aber keine der Bedeutungen entspricht auch nur annähernd dem, was Sie hier wirklich wollen.)

Da wir wissen, dass dies .„beliebiges einzelnes Zeichen“ bedeutet, können wir Folgendes tun: grep 'This.*String' file.txtJetzt wird der grep-Befehl es korrekt lesen: Thisgefolgt von einem beliebigen Zeichen (stellen Sie sich das als eine Auswahl von ASCII-Zeichen vor), das beliebig oft wiederholt wird, gefolgt von String.

Antwort2

Das *Metazeichen in BRE 1 s, ERE 1 s und PCRE 1 s stimmt mit 0 oder mehr Vorkommen des zuvor gruppierten Musters überein (wenn dem *Metazeichen ein gruppiertes Muster vorangeht), 0 oder mehr Vorkommen der vorherigen Zeichenklasse (wenn dem Metazeichen eine Zeichenklasse vorangeht *) oder 0 oder mehr Vorkommen des vorherigen Zeichens (wenn dem *Metazeichen weder ein gruppiertes Muster noch eine Zeichenklasse vorangeht);

Dies bedeutet, dass im This*StringMuster das *Metazeichen, dem weder ein gruppiertes Muster noch eine Zeichenklasse vorangestellt ist, *mit 0 oder mehr Vorkommen des vorherigen Zeichens übereinstimmt (in diesem Fall mit dem sZeichen):

% cat infile               
ThisExampleString
ThisString
ThissString
% grep 'This*String' infile
ThisString
ThissString

Um 0 oder mehr Vorkommen eines beliebigen Zeichens abzugleichen, möchten Sie 0 oder mehr Vorkommen des .Metazeichens abgleichen, das jedem Zeichen entspricht:

% cat infile               
ThisExampleString
% grep 'This.*String' infile
ThisExampleString

Das *Metazeichen in BREs und EREs ist immer „gierig“, d. h. es wird mit der längsten Übereinstimmung abgeglichen:

% cat infile
ThisExampleStringIsAString
% grep -o 'This.*String' infile
ThisExampleStringIsAString

Dies ist möglicherweise nicht das gewünschte Verhalten. Ist dies nicht der Fall, können Sie grepdie PCRE-Engine von einschalten (mit der -POption) und das ?Metazeichen anhängen, das, wenn es nach den *und +Metazeichen eingefügt wird, deren Gier ändert:

% cat infile
ThisExampleStringIsAString
% grep -Po 'This.*?String' infile
ThisExampleString

1: Grundlegende reguläre Ausdrücke, erweiterte reguläre Ausdrücke und Perl-kompatible reguläre Ausdrücke

Antwort3

Eine der Erklärungen finden Sie hierVerknüpfung:

Das Sternchen " *" hat in regulären Ausdrücken nicht die gleiche Bedeutung wie bei Platzhaltern. Es ist ein Modifikator, der auf das vorangehende einzelne Zeichen oder einen Ausdruck wie [0-9] angewendet wird. Ein Sternchen entspricht keinem oder mehreren der vorangehenden Zeichen. Somit entspricht " " [A-Z]*einer beliebigen Anzahl von Großbuchstaben, auch keinem, während " [A-Z][A-Z]*einem oder mehreren Großbuchstaben entspricht.

Antwort4

*hat eine besondere Bedeutung sowohl als SchaleGlotzenZeichen ("Platzhalter") und als regulärer AusdruckMetazeichenSie müssen beides berücksichtigen, aber wenn SieZitatIhren regulären Ausdruck, dann können Sie verhindern, dass die Shell ihn speziell behandelt und sicherstellen, dass sie ihn unverändert angrep. Obwohleine Artkonzeptionell ähnlich, *ist die Bedeutung für die Shell etwas völlig anderes als die Bedeutung für grep.

ErsteDie Shell behandelt es *als Platzhalter.

Du sagtest:

Es spielt keine Rolle, ob der Ausdruck in Anführungszeichen eingeschlossen ist.

Das hängt davon ab, welche Dateien in dem Verzeichnis vorhanden sind, in dem Sie sich gerade befinden, wenn Sie den Befehl ausführen. Bei Mustern, die das Verzeichnistrennzeichen enthalten /, kann es davon abhängen, welche Dateien in Ihrem gesamten System vorhanden sind. Sie sollten immerZitatreguläre Ausdrücke für grep--andEinzelzitatesind normalerweise am besten--es sei dennSie sind sicher, dass Sie einverstanden sind mit derneun Arten potenziell überraschender Transformationendie Schale funktioniert ansonstenVorAusführen des grepBefehls.

Wenn die Shell auf ein *Zeichen stößt, das nichtzitiert, es bedeutet "null oder mehr beliebige Zeichen" undersetzt das Wort, das es enthältmit einer Liste von Dateinamen, die dem Muster entsprechen. (Dateinamen, die mit beginnen, .sind ausgeschlossen – es sei denn, Ihr Muster selbst beginnt mit. oderSie haben Ihre Shell so konfiguriert, dass sie diese ohnehin einschließt.) Dies wird alsGlotzen--und auch durch die NamenDateinamenerweiterungUndPfadnamenerweiterung.

Der Effekt grepbesteht normalerweise darin, dass der erste passende Dateiname als regulärer Ausdruck verwendet wird - auch wenn dies für einen menschlichen Leser offensichtlich ist.nichtals regulärer Ausdruck gedacht - während alle anderen automatisch von Ihrem Glob aufgelisteten Dateinamen als Dateien genommen werdeninnenin dem nach Übereinstimmungen gesucht werden soll. (Sie sehen die Liste nicht, sie wird undurchsichtig an weitergegeben grep.) Sie möchten praktisch nie, dass dies geschieht.

Der Grund dafür istManchmalkein Problem - und in Ihrem speziellen Fall zumindestbis jetzt, es war nicht--wird das *in Ruhe gelassenwenn alle der folgenden Punkte zutreffen:

  1. Es gabNEINDateien, deren Namen übereinstimmten. ...OderSie haben Globbing in Ihrer Shell deaktiviert, normalerweise mit set -foder dem Äquivalent set -o noglob. Dies kommt jedoch selten vor und Sie wissen wahrscheinlich, dass Sie es getan haben.

  2. Sie verwenden eine Shell, deren Standardverhalten darin besteht, sie *in Ruhe zu lassen, wenn keine passenden Dateinamen vorhanden sind. Dies ist der Fall bei Bash, das Siewahrscheinlichverwendet, aber nicht in allen Shells im Bourne-Stil. (Das Standardverhalten in der beliebten Shell Zsh ist beispielsweise, dass Globs entweder(A)erweitern oder(B)einen Fehler erzeugen.)...OderSie haben dieses Verhalten Ihrer Shell geändert. Die Art und Weise, wie das geschieht, ist von Shell zu Shell unterschiedlich.

  3. Du hast nichtansonstenhat Ihrer Shell gesagt, dass sie das Ersetzen von Globs durchNichtswenn keine passenden Dateien vorhanden sind, noch in dieser Situation mit einer Fehlermeldung abzubrechen. In Bash wäre dies durch die Aktivierung von nullgloboder geschehen.failglob Shell-Option, jeweils.

Auf Nr. 2 und 3 können Sie sich manchmal verlassen, auf Nr. 1 jedoch nur selten. Ein grepBefehl mit einem nicht in Anführungszeichen gesetzten Muster, der jetzt funktioniert, funktioniert möglicherweise nicht mehr, wenn Sie andere Dateien haben oder ihn von einem anderen Ort aus ausführen.Zitieren Sie Ihren regulären Ausdruck und das Problem verschwindet.

DannDer grepBefehl behandelt es *als einen Quantifizierer.

Die anderen Antworten - wie etwa dievon Sergiy KolodyazhnyyUndvon kos--gehen auch auf diesen Aspekt der Frage ein, wenn auch auf etwas andere Weise. Daher möchte ich diejenigen, die sie noch nicht gelesen haben, dazu ermutigen, dies zu tun, entweder bevor oder nachdem sie den Rest dieser Antwort gelesen haben.

Angenommen, das *gelangt zu grep - was durch Anführungszeichen sichergestellt werden sollte -, grepdann bedeutet dies, dassdas vorangehende Elementkann beliebig oft vorkommen, anstatt genau einmal auftreten zu müssen. Es könnte trotzdem einmal vorkommen. Oder es könnte überhaupt nicht vorhanden sein. Oder es könnte wiederholt werden. Text, der dazu passtbeliebigdieser Möglichkeiten wird abgeglichen.

Was meine ich mit „Artikel“?

  • Ein einzelnerCharakter. Da bmit einem wörtlichen übereinstimmt b, b*stimmt mit null oder mehr bs überein, ab*cstimmt also mit ac, abc, abbc, abbbc, usw. überein.

    Ebenso, da.passt zu jedem Zeichen, .*entspricht null oder mehr Zeichen1, a.*centspricht also ac, akc, ahjglhdfjkdlgjdfkshlgc, sogar acccccchjckhcc, usw.Oder

  • AZeichenklasse. Da mit oder [xy]übereinstimmt , stimmt auch mit null oder mehr Zeichen überein, von denen jedes entweder oder ist, und stimmt somit mit , , , , , , , , , , usw. überein.xy[xy]*xyp[xy]*qpqpxqpyqpxxqpxyqpyxqpyyqpxxxqpxxyq

    das gilt auch fürKurzformvon Zeichenklassen wie \w, \W, \s, und \S. Da mit \wjedem Wortzeichen übereinstimmt, \w*stimmt mit null oder mehr Wortzeichen überein.Oder

  • AGruppe. Da \(bar\)mit übereinstimmt bar, \(bar\)*entspricht null oder mehr bars, foo\(bar\)*bazentspricht also mit foobaz, foobarbaz, foobarbarbaz, foobarbarbarbaz, usw.

    Mit den Optionen -Eoder wird Ihr regulärer Ausdruck als-PgrepEHEoderPCREbzw. alsBRE, und Gruppen werden dann von ( )statt umgeben \( \), sodass Sie dann (bar)statt \(bar\)und foo(bar)bazstatt verwenden würden foo\(bar\)baz.

man grepgibt am Ende eine einigermaßen verständliche Erklärung der BRE- und ERE-Syntax und listet am Anfang alle akzeptierten Befehlszeilenoptionen auf grep. Ich empfehle diese Handbuchseite als Ressource und auchdie GNU Grep-DokumentationUnddieses Tutorial/diese Referenzseite(zu dem ich oben eine Reihe von Seiten verlinkt habe).

Zum Testen und Lernen grepempfehle ich, es mit einem Muster, aber ohne Dateinamen aufzurufen. Dann nimmt es die Eingabe von Ihrem Terminal entgegen. Geben Sie Zeilen ein; die Zeilen, die Ihnen zurückgesendet werden, sind diejenigen, die Text enthielten, der mit Ihrem Muster übereinstimmte. Zum Beenden drücken Sie Ctrl+ am Anfang einer Zeile, was das Ende der Eingabe signalisiert. (Oder Sie können + Ddrücken, wie bei den meisten Befehlszeilenprogrammen.) Beispiel:CtrlC

grep 'This.*String'

Wenn Sie die --colorFlagge verwenden, grepwird die spezifischeTeileIhrer Zeilen, die mit Ihrem regulären Ausdruck übereinstimmen, was sehr nützlich ist, um herauszufinden, was ein regulärer Ausdruck bewirkt, und um zu finden, wonach Sie suchen, wenn Sie es herausgefunden haben. Standardmäßig haben Ubuntu-Benutzer einen Bash-Alias, der ausgeführt wird grep --color=auto– was für diesen Zweck ausreichend ist –, wenn Sie grepvon der Befehlszeile aus ausführen, sodass Sie wahrscheinlich nicht einmal manuell übergeben müssen --color.

1 Daher .*bedeutet in einem regulären Ausdruck, was *in einem Shell-Glob bedeutet. Der Unterschied besteht jedoch darin, dass grepautomatisch Zeilen gedruckt werden, die Ihre Übereinstimmung enthaltenüberallin ihnen, daher ist es normalerweise nicht notwendig, es .*am Anfang oder Ende eines regulären Ausdrucks zu haben.

verwandte Informationen