grep überspringe n Zeilen der Datei und suche nur danach

8

Ich habe eine riesige Protokolldatei und möchte das erste Auftreten eines Musters erfassen und direkt nach diesem Auftreten ein anderes Muster finden .

Zum Beispiel:

123
XXY
214
ABC
182
558
ABC
856
ABC

In meinem Beispiel möchte ich 182das nächste Vorkommen von finden und dann findenABC

Das erste Vorkommen ist einfach:

grep -n -m1 "182" /var/log/file

Dies gibt aus:

5:182

Wie finde ich das nächste Auftreten von ABC?

Meine Idee war es grep, die ersten nZeilen (im obigen Beispiel n=5) zu überspringen , basierend auf der Zeilennummer 182. Aber wie mache ich das?

koljanep
quelle
1
Ist es eine Anforderung, grepdie verwendet wird? Ich denke nicht, dass dies möglich ist, grepaber es wäre einfach mit awkoder sed(allein oder in Kombination mit grep).
Hauke ​​Laging
@HaukeLaging grepist nicht erforderlich. Ich bin noch nicht so vertraut mit sedoder awk. Wenn Sie eine gute Lösung haben, lassen Sie es mich hören! :) @don_crissti nur die erste Zeile sollte gedruckt werden. Die anderen Vorkommnisse sind mir egal.
Koljanep

Antworten:

10

Mit können sedSie eine Range- und qUIT-Eingabe an einem einzigen Abschluss verwenden:

sed '/^182$/p;//,/^ABC$/!d;/^ABC$/!d;q'

Ebenso können Sie mit GNU grepdie Eingabe auf zwei greps aufteilen :

{ grep -nxF -m1 182; grep -nxF -m1 ABC; } <<\IN
123
XXY
214
ABC
182
558
ABC
856
ABC
IN

... was druckt ...

5:182
2:ABC

... um anzuzeigen , dass die erste grepgefunden hat, dass ein -FLiteral mit festen Zeichenfolgen, die -xgesamte Zeile 182 , 5 Zeilen vom Beginn des Lesens an übereinstimmt, und die zweite eine ähnlich typisierte ABC- Übereinstimmung 2 Zeilen vom Beginn des Lesens gefunden hat - oder 2 Zeilen nach dem ersten grep Abbruch in Zeile 5.

Von man grep:

-m NUM, --max-count=NUM
          Stop  reading  a  file  after  NUM  matching
          lines.   If the input is standard input from
          a regular file, and NUM matching  lines  are
          output, grep ensures that the standard input
          is  positioned  to  just  after   the   last
          matching  line before exiting, regardless of
          the  presence  of  trailing  context  lines.
          This  enables  a calling process to resume a
          search. 

Ich habe ein Dokument hier verwendet, um eine reproduzierbare Demonstration zu ermöglichen, aber Sie sollten wahrscheinlich Folgendes tun:

{ grep ...; grep ...; } </path/to/log.file

Es funktioniert auch mit anderen Shell-Verbundbefehlskonstrukten wie:

for p in 182 ABC; do grep -nxFm1 "$p"; done </path/to/log.file
mikeserv
quelle
+1 Hab das auf der Manpage gesehen. Das habe ich versucht, nur mit einer Pipe zwischen den grep's anstelle eines ;... no-go
Xen2050
@ Xen2050 - die Pipe funktioniert normalerweise nicht - eine lseekable-Datei ist normalerweise das, was Sie möchten, wenn Sie Eingaben freigeben.
Mikesserv
Beeindruckende Antwort, aber ich unterstütze Ihre Aussage zu Pipelines nicht. Das hier veröffentlichte Dokument, das die beiden grepgemeinsam nutzen, ist praktisch eine Pipeline für sie. Noch etwas: Ich habe es versucht, ohne die Markierungslinie zu drucken, aber es ist sed '//,/^ABC$/!d;/^ABC$/!d;q'ein seltsamer Fehler aufgetreten. Was macht //das
Hauke ​​Laging
1
@HaukeLaging - das Here-Dokument (in den meisten Shells) ist keine Pipe - es ist eine echte tmp-Datei, die von der Shell erstellt wurde und die die Shell vor dem Schreiben löscht - unter Beibehaltung des Deskriptors. Es ist immer noch suchbar. Rohre sind im Allgemeinen nicht suchbar. Ich werde mir das sedDing ansehen - habe es einfach ganz schnell ausgeschrieben.
mikeserv
1
@HaukeLaging - Oh, das sedDing funktioniert also - du hast die Referenz einfach weggelassen . In können sedSie /address/mit einer leeren //Adresse wieder auf den letzten verweisen . Also /^182$/command;//,/next_address/einfach /^182$/command;/^182$/,/next_address/. Ihr Fehler war wahrscheinlich kein vorheriger regulärer Ausdruck, wenn Sie eine GNU verwendeten sed. Das Pipe-Lseek-Ding kann übrigens durch Indirektion über die /dev/fd/[num]Links auf Linux-Systemen manipuliert werden - aber wenn Sie nicht sehr vorsichtig sind, mit Puffern gut umzugehen (wie bei dd) , ist das normalerweise ein verlorener Kampf.
Mikeserv
2

Verwendung grepmit Perl-kompatiblen regulären Ausdrücken ( pcregrep):

pcregrep -Mo '182(.|\n)*?\KABC'

Mit dieser Option kann -Mdas Muster mit mehr als einer Zeile \Kübereinstimmen und enthält kein übereinstimmendes Muster (bis zu diesem Punkt) in der Ausgabe. Sie können entfernen, \Kwenn Sie die gesamte Region als Ergebnis haben möchten.

jimmij
quelle
2
> awk '/^182$/ { startline=1; }; startline == 0 { next; }; /^ABC$/ { print "line " NR ": " $0; exit; }' file
line 7: ABC
Hauke ​​Laging
quelle
1
Das gibt das erste ABC irgendwo ; Diese Frage möchte das erste ABC nach dem ersten 182. Am direktesten ist ein Flag wie awk '/^182$/{z=1;next} z&&/^ABC$/{print NR":"$0;exit}' file- oder Sie können mindestens eine explizite getline()Schleife schreiben, die normalerweise ungeschickter ist, oder mit einem Bereich, der fast wie @ JRFergusons Perl ist, klug (?) sein:awk '!x&&/^182$/,/^ABC$/ {x=NR":"$0} END{print x}
dave_thompson_085
@ dave_thompson_085 In der Tat. Richtige Idee, aber furchtbar codiert (zwei Ideen beim Schreiben verwechselt). Es war mir sogar peinlich, aber ich wunderte mich nicht über die Ausgabe.
Hauke ​​Laging
1

Eine Perl-Variante, die Sie verwenden können, ist:

perl -nle 'm/182/../ABC/ and print' file

... die Linien im passenden Bereich druckt.

Wenn Ihre Datei mehr als einen übereinstimmenden Bereich enthielt, können Sie die Ausgabe nur auf den ersten Bereich beschränken, indem Sie das /Trennzeichen auf ändern?

perl -nle 'm?182?..?ABC? and print'
JRFerguson
quelle
1

Bleib bei Just grepund füge tail& hinzu cut, du könntest ...

grep für die Zeilennummer der ersten Übereinstimmung von 182:

grep -m 1 -n 182 /var/log/file |cut -f1 -d:

Verwenden , die für die ganze grep ABCist erst nach der ersten passenden Zeile oberhalb, unter Verwendung von tails -n +Kzur Ausgabe nach der K - ten Zeile. Alle zusammen:

tail -n +$(grep -m 1 -n 182 /var/log/file |cut -f1 -d:) /var/log/file | grep ABC

Oder fügen Sie -m 1erneut hinzu, um nur die erste Übereinstimmung zu findenABC

tail -n +$(grep -m 1 -n 182 /var/log/file|cut -f1 -d:) /var/log/file|grep -m 1 ABC

Verweise:
manSeiten
/programming/6958841/use-grep-to-report-back-only-line-numbers

Xen2050
quelle
1

Eine andere Variante ist diese:

grep -n -A99999 "182" /var/log/file|grep -n -m1 "ABC"

Die Flagge -An greift nach dem Spiel n Zeilen und 99999 soll nur sichergehen, dass wir nichts verpassen. Größere Dateien sollten mehr Zeilen haben (überprüfen Sie mit "wc -l").

Fabbe
quelle
0

Der Bereichsoperator ,kann hier verwendet werden:

< yourfile \
sed -e '
   /182/,/ABC/!d
   //!d;=;/ABC/q
' | sed -e 'N;s/\n/:/'

Der Bereichsoperator kann ..zusammen mit dem Nur-einmal-Match-Operator m??hier in verwendet werdenPerl

perl -lne 'm?182? .. m?ABC? and print "$.:$_" if /182/ || /ABC/' yourfile

quelle