Wenn ich ein Dokument greppe, das Folgendes enthält:
ThisExampleString
...für den Ausdruck This*String
oder *String
wird 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*String
und *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.txt
versuchen Sie zu sagen: „Hey, grep, finde für mich das Wort Thi
, gefolgt von einem Kleinbuchstaben s
null- oder mehrmals, gefolgt vom Wort String
". Der Kleinbuchstabe s
ist in nirgends zu finden Example
, daher ignoriert grep ThisExampleString
.
Im Fall von grep '*String' file.txt
sagen Sie: „grep, finde den leeren String – buchstäblich nichts – vor dem Wort String
„. Natürlich ist das nicht die Art, wie ThisExampleString
es 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.txt
Jetzt wird der grep-Befehl es korrekt lesen: This
gefolgt 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*String
Muster 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 s
Zeichen):
% 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 grep
die PCRE-Engine von einschalten (mit der -P
Option) 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 grep
Befehls.
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 grep
besteht 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:
Es gabNEINDateien, deren Namen übereinstimmten. ...OderSie haben Globbing in Ihrer Shell deaktiviert, normalerweise mit
set -f
oder dem Äquivalentset -o noglob
. Dies kommt jedoch selten vor und Sie wissen wahrscheinlich, dass Sie es getan haben.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.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
nullglob
oder geschehen.failglob
Shell-Option, jeweils.
Auf Nr. 2 und 3 können Sie sich manchmal verlassen, auf Nr. 1 jedoch nur selten. Ein grep
Befehl 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 grep
Befehl 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 -, grep
dann 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
b
mit einem wörtlichen übereinstimmtb
,b*
stimmt mit null oder mehrb
s überein,ab*c
stimmt also mitac
,abc
,abbc
,abbbc
, usw. überein.Ebenso, da
.
passt zu jedem Zeichen,.*
entspricht null oder mehr Zeichen1,a.*c
entspricht alsoac
,akc
,ahjglhdfjkdlgjdfkshlgc
, sogaracccccchjckhcc
, usw.OderAZeichenklasse. 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.x
y
[xy]*
x
y
p[xy]*q
pq
pxq
pyq
pxxq
pxyq
pyxq
pyyq
pxxxq
pxxyq
das gilt auch fürKurzformvon Zeichenklassen wie
\w
,\W
,\s
, und\S
. Da mit\w
jedem Wortzeichen übereinstimmt,\w*
stimmt mit null oder mehr Wortzeichen überein.OderAGruppe. Da
\(bar\)
mit übereinstimmtbar
,\(bar\)*
entspricht null oder mehrbar
s,foo\(bar\)*baz
entspricht also mitfoobaz
,foobarbaz
,foobarbarbaz
,foobarbarbarbaz
, usw.Mit den Optionen
-E
oder wird Ihr regulärer Ausdruck als-P
grep
EHEoderPCREbzw. alsBRE, und Gruppen werden dann von(
)
statt umgeben\(
\)
, sodass Sie dann(bar)
statt\(bar\)
undfoo(bar)baz
statt verwenden würdenfoo\(bar\)baz
.
man grep
gibt 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 grep
empfehle 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 --color
Flagge verwenden, grep
wird 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 grep
von 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 grep
automatisch 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.