Ich lerne Linux und habe eine Herausforderung, die ich anscheinend nicht alleine lösen kann. Hier ist es:
Grep eine Zeile aus einer Datei, die 4 Zahlen in einer Reihe, aber nicht mehr als 4 enthält.
Ich bin mir nicht sicher, wie ich das angehen soll. Ich kann nach bestimmten Zahlen suchen, aber nicht nach deren Anzahl in einer Zeichenfolge.
command-line
grep
text-processing
Buddha
quelle
quelle
1234a12345
angezeigt werden oder nicht?\b\d{4}\b
Antworten:
Es gibt zwei Möglichkeiten, diese Frage zu interpretieren. Ich werde beide Fälle ansprechen. Möglicherweise möchten Sie Zeilen anzeigen:
Zum Beispiel würde (1) angezeigt
1234a56789
, aber (2) nicht.Wenn Sie alle Zeilen anzeigen möchten, die eine Folge von vier Ziffern enthalten, die selbst nicht zu einer längeren Folge von Ziffern gehört, haben Sie folgende Möglichkeiten:
Dies verwendet reguläre Perl-Ausdrücke , die Ubuntu
grep
( GNU grep ) über unterstützt-P
. Es passt weder zu Text wie12345
, noch zu dem1234
oder dem2345
, der Teil davon ist. Aber es wird dem1234
in entsprechen1234a56789
.In Perl reguläre Ausdrücke:
\d
bedeutet eine beliebige Ziffer (es ist ein kurzer Weg, um[0-9]
oder zu sagen[[:digit:]]
).x{4}
Spielex
4 mal. (Die{
}
Syntax ist nicht spezifisch für reguläre Perl-Ausdrücke, sondern auch für erweiterte reguläre Ausdrücke übergrep -E
.) So\d{4}
ist es auch mit\d\d\d\d
.(?<!\d)
ist eine negative Look-Behind-Behauptung mit der Breite Null. Es bedeutet "sofern nicht vorangestellt"\d
".(?!\d)
ist eine negative Vorausschau-Behauptung mit der Breite Null. Es bedeutet "sofern nicht gefolgt von\d
".(?<!\d)
und(?!\d)
stimmen Sie nicht mit Text außerhalb der vierstelligen Reihenfolge überein. Stattdessen verhindern sie (wenn sie zusammen verwendet werden), dass eine Folge von vier Ziffern übereinstimmt, wenn sie Teil einer längeren Folge von Ziffern ist.Nur den Look-Behind oder nur den Look-Ahead zu verwenden, ist unzureichend, da die ganz rechts oder ganz links liegende vierstellige Teilfolge immer noch übereinstimmen würde.
Ein Vorteil der Verwendung von Look-Behind- und Look-Ahead-Behauptungen besteht darin, dass Ihr Muster nur den vierstelligen Folgen selbst und nicht dem umgebenden Text entspricht. Dies ist hilfreich bei der Verwendung der Farbmarkierung (mit der
--color
Option).Standardmäßig hat jeder Benutzer in Ubuntu
alias grep='grep --color=auto'
eine~.bashrc
Datei . Sie erhalten also automatisch eine farbige Hervorhebung, wenn Sie einen einfachen Befehl ausführen, der mitgrep
(dies ist, wenn Aliase erweitert werden) beginnt, und die Standardausgabe ist ein Terminal (das prüft, ob). Übereinstimmungen sind in der Regel rot hervorgehoben (in der Nähe von Zinnoberrot ), aber ich habe es in Fettschrift kursiv dargestellt. Hier ist ein Screenshot:--color=auto
Und Sie können sogar
grep
nur passenden Text und nicht die ganze Zeile drucken lassen-o
:Alternativer Weg, ohne hinterherzuschauen und vorausschauende Behauptungen
Wenn Sie jedoch:
grep
nicht unterstützt-P
oder auf andere Weise nicht mag , dass ein Perl regulären Ausdrücke verwenden, und... dann können Sie dies stattdessen mit einem erweiterten regulären Ausdruck erreichen :
Dies entspricht vier Ziffern und dem sie umgebenden nichtstelligen Zeichen - oder dem Anfang oder Ende der Zeile. Speziell:
[0-9]
Entspricht einer beliebigen Ziffer (wie[[:digit:]]
oder\d
in regulären Perl-Ausdrücken) und{4}
bedeutet "viermal". Entspricht also[0-9]{4}
einer vierstelligen Folge.[^0-9]
Sucht nach Zeichen, die nicht im Bereich von0
bis liegen9
. Es ist äquivalent zu[^[:digit:]]
(oder\D
in regulären Perl-Ausdrücken).^
Wenn es nicht in[
]
Klammern steht, stimmt es mit dem Anfang einer Zeile überein. Entspricht$
dem Ende einer Zeile.|
bedeutet oder und Klammern sind für die Gruppierung (wie in der Algebra). Entspricht also(^|[^0-9])
dem Zeilenanfang oder einem nichtstelligen Zeichen, während das Zeilenende oder ein nichtstelliges Zeichen($|[^0-9])
übereinstimmt.Übereinstimmungen treten also nur in Zeilen auf, die eine vierstellige Sequenz (
[0-9]{4}
) enthalten, die gleichzeitig ist:(^|[^0-9])
) und($|[^0-9])
).Wenn Sie dagegen alle Zeilen anzeigen möchten, die eine vierstellige Folge enthalten, aber keine Folge mit mehr als vier Ziffern enthalten (auch keine , die von einer anderen Folge mit nur vier Ziffern getrennt ist), dann ist dies konzeptionell Ihre Aufgabe Ziel ist es, Linien zu finden, die zu einem Muster passen, aber nicht zu einem anderen.
Daher würde ich, selbst wenn Sie wissen, wie man es mit einem einzelnen Muster macht, vorschlagen, so etwas wie Matts zweiten Vorschlag zu verwenden.
grep
für die beiden Muster separat gilt.Dabei profitieren Sie nicht stark von den erweiterten Funktionen der regulären Perl-Ausdrücke. Daher ziehen Sie es möglicherweise vor, diese nicht zu verwenden. In Übereinstimmung mit dem obigen Stil ist hier eine Verkürzung der Matt-Lösung mit
\d
(und geschweiften Klammern) anstelle von[0-9]
:Da matt's way verwendet wird
[0-9]
, ist es portabler - es funktioniert auf Systemen, auf denen reguläre Perl-Ausdrücke nicht unterstützt werden. Wenn Sie (oder ) anstelle von verwenden , aber weiterhin verwenden , erhalten Sie die Portabilität von Matt's Way etwas präziser:grep
[0-9]
[[:digit:]]
\d
{
}
Alternative Methode mit einem einzigen Muster
Wenn Sie wirklich einen
grep
Befehl bevorzugen , dergrep
durch ein Pipe getrennte s) wie oben)... dann können Sie verwenden:
Das
-x
Flag bewirkt,grep
dass nur Zeilen angezeigt werden, bei denen die gesamte Zeile übereinstimmt (und keine Zeile, die eine Übereinstimmung enthält).Ich habe ein Perl regulären Ausdrücke verwendet , weil ich die Kürze denke
\d
und im\D
Wesentlichen Klarheit in diesem Fall erhöhen. Wenn Sie jedoch etwas Tragbares für Systeme benötigen,grep
die nicht unterstützt werden-P
, können Sie diese durch[0-9]
und[^0-9]
(oder durch[[:digit:]]
und[^[:digit]]
) ersetzen :Diese regulären Ausdrücke funktionieren folgendermaßen:
In der Mitte
\d{4}
oder[0-9]{4}
entspricht einer Folge von vier Ziffern. Wir haben vielleicht mehr als eine davon, aber wir müssen mindestens eine haben.Auf der linken Seite
(\d{0,4}\D)*
oder([0-9]{0,4}[^0-9])*
entspricht null oder mehr (*
) Instanzen von nicht mehr als vier Ziffern, gefolgt von einer Nicht-Ziffer. Nullstellen (dh nichts) ist eine Möglichkeit für "nicht mehr als vier Stellen". Dies entspricht (a) der leeren Zeichenfolge oder (b) einer Zeichenfolge, die nicht mit einer Ziffer endet und keine Sequenzen mit mehr als vier Ziffern enthält.Da der Text unmittelbar links von der Mitte
\d{4}
(oder[0-9]{4}
) entweder leer sein oder mit einer Nicht-Ziffer enden muss, wird verhindert, dass die Zentrale\d{4}
vier Ziffern mit einer weiteren (fünften) Ziffer links davon abgleichen kann.Auf der rechten Seite
(\D\d{0,4})*
oder([^0-9][0-9]{0,4})*
entspricht null oder mehr (*
) Instanzen einer Nicht-Ziffer, gefolgt von nicht mehr als vier Ziffern (die wie zuvor vier, drei, zwei, eins oder gar keine sein können). Dies entspricht (a) der leeren Zeichenfolge oder (b) einer Zeichenfolge, die nicht mit einer Ziffer beginnt und keine Sequenzen mit mehr als vier Ziffern enthält.Da der Text unmittelbar rechts von der Mitte
\d{4}
(oder[0-9]{4}
) entweder leer sein muss oder mit einer Nicht-Ziffer beginnen muss, wird verhindert, dass die Zentrale\d{4}
vier Ziffern mit einer weiteren (fünften) Ziffer rechts davon abgleichen kann.Dies stellt sicher, dass irgendwo eine vierstellige Folge vorhanden ist und dass nirgendwo eine Folge von fünf oder mehr Ziffern vorhanden ist.
Es ist nicht schlecht oder falsch, es so zu machen. Der vielleicht wichtigste Grund, diese Alternative in Betracht zu ziehen, besteht darin, den Nutzen der Verwendung (oder ähnlicher) zu verdeutlichen , wie oben und in Matts Antwort vorgeschlagen .
grep -P '\d{4}' file | grep -Pv '\d{5}'
Auf diese Weise ist es klar, dass Ihr Ziel darin besteht, Zeilen auszuwählen, die eine Sache, aber keine andere enthalten. Außerdem ist die Syntax einfacher (so dass sie von vielen Lesern / Betreuern möglicherweise schneller verstanden wird).
quelle
Dies zeigt Ihnen 4 Zahlen hintereinander, aber nicht mehr
Beachten Sie, dass ^ nicht bedeutet
Es gibt ein Problem damit, obwohl ich nicht sicher bin, wie ich es beheben soll ... Wenn die Nummer das Ende der Zeile ist, wird sie nicht angezeigt.
Diese hässlichere Version würde jedoch für diesen Fall funktionieren
quelle
a12345b
, weil er passt2345b
.Wenn
grep
reguläre Perl-Ausdrücke (-P
) nicht unterstützt werden , verwenden Sie den folgenden Shell-Befehl:wo
printf '[0-9]%.0s' {1..4}
wird 4 mal produzieren[0-9]
. Diese Methode ist nützlich, wenn Sie lange Ziffern haben und das Muster nicht wiederholen möchten (ersetzen4
Sie es einfach durch die Anzahl der zu suchenden Ziffern).Mit
-w
wird nach den ganzen Wörtern gesucht. Wenn Sie jedoch an alphanumerischen Zeichenfolgen interessiert sind, wie z. B.1234a
, fügen Sie diese[^0-9]
am Ende des Musters hinzu, zVerwenden
$()
ist im Grunde eine Befehlsersetzung . Überprüfen Sie diesen Beitrag, um zu sehen, wie sichprintf
das Muster wiederholt.quelle
Sie können den folgenden Befehl versuchen, indem Sie ihn
file
durch den tatsächlichen Dateinamen in Ihrem System ersetzen:Sie können auch überprüfen diesem Lernprogramm auch nach weiteren Verwendungen des Befehls grep suchen.
quelle