Es gibt zwei Möglichkeiten, diese Frage zu interpretieren. Ich werde beide Fälle ansprechen. Möglicherweise möchten Sie Zeilen anzeigen:
- die eine Folge von vier Ziffern enthalten, die selbst nicht Teil einer längeren Folge von Ziffern ist, oder
- das enthält eine vierstellige Folge aber keine Ziffernfolge mehr (auch nicht einzeln).
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:
grep -P '(?<!\d)\d{4}(?!\d)' file
Dies verwendet reguläre Perl-Ausdrücke , die Ubuntu grep
( GNU grep ) über unterstützt -P
. Es passt weder zu Text wie 12345
, noch zu dem 1234
oder dem 2345
, der Teil davon ist. Aber es wird dem 1234
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}
Spiele x
4 mal. (Die {
}
Syntax ist nicht spezifisch für reguläre Perl-Ausdrücke, sondern auch für erweiterte reguläre Ausdrücke über grep -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).
ek@Io:~$ grep -P '(?<!\d)\d{4}(?!\d)' <<< 12345abc789d0123e4
12345abc789d0123e4
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 mit grep
(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
:
ek@Io:~$ grep -oP '(?<!\d)\d{4}(?!\d)' <<< 12345abc789d0123e4
0123
Alternativer Weg, ohne hinterherzuschauen und vorausschauende Behauptungen
Wenn Sie jedoch:
- benötigt einen Befehl, der auch auf Systeme ausgeführt werden , wo
grep
nicht unterstützt -P
oder auf andere Weise nicht mag , dass ein Perl regulären Ausdrücke verwenden, und
- Sie müssen die vier Ziffern nicht speziell abgleichen. Dies ist normalerweise der Fall, wenn Sie nur Zeilen mit Übereinstimmungen und anzeigen möchten
- sind in Ordnung mit einer Lösung, die ein bisschen weniger elegant ist
... dann können Sie dies stattdessen mit einem erweiterten regulären Ausdruck erreichen :
grep -E '(^|[^0-9])[0-9]{4}($|[^0-9])' file
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 von 0
bis liegen 9
. 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:
- am Anfang der Zeile oder mit vorangestelltem Zeichen (
(^|[^0-9])
) und
- am Ende der Zeile oder gefolgt von einer Nicht-Ziffer (
($|[^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]
:
grep -P '\d{4}' file | grep -Pv '\d{5}'
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
{
}
grep -E '[0-9]{4}' file | grep -Ev '[0-9]{5}'
Alternative Methode mit einem einzigen Muster
Wenn Sie wirklich einen grep
Befehl bevorzugen , der
- verwendet einen einzelnen regulären Ausdruck (nicht zwei
grep
durch ein Pipe getrennte s) wie oben)
- um Zeilen anzuzeigen, die mindestens eine Folge von vier Ziffern enthalten,
- aber keine Sequenzen von fünf (oder mehr) Ziffern,
- und es macht Ihnen nichts aus, die ganze Zeile abzugleichen, nicht nur die Ziffern (das stört Sie wahrscheinlich nicht)
... dann können Sie verwenden:
grep -Px '(\d{0,4}\D)*\d{4}(\D\d{0,4})*' file
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 :
grep -Ex '([0-9]{0,4}[^0-9])*[0-9]{4}([^0-9][0-9]{0,4})*' file
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).
1234a12345
angezeigt werden oder nicht?