Wie kann ich in einer Datei nach Text suchen und den Absatz mit dem Text anzeigen?


24

Unten ist der Text in der Datei:

Pseudo name=Apple
Code=42B
state=fault

Pseudo name=Prance
Code=43B
state=good

Ich muss nach "42B" suchen und die Ausgabe aus dem obigen Text wie folgt erhalten:

Pseudo name=Apple
Code=42B
state=fault

Hat jemand eine Idee, wie dies mit grep/ awk/ erreicht werden kann sed?


Sie haben diese Frage mit "grep" markiert. Suchen Sie dann nur nach "grep" -Lösungen? In der Frage geben Sie auch awk & sed an. Können wir diese Tags hinzufügen? Ich war mir Ihrer Absicht nicht sicher, als ich die Frage gestern Abend bearbeitete.
SLM

Antworten:


38

Mit awk

awk -v RS='' '/42B/' file

RS=Ändert das Trennzeichen für Eingabedatensätze von einer Zeilenvorschubzeile in Leerzeilen. Wenn ein Feld in einem Datensatz enthält, /42B/drucken Sie den Datensatz.

''(die Nullzeichenfolge) ist ein magischer Wert, der verwendet wird, um Leerzeilen gemäß POSIX darzustellen :

Wenn RS null ist, werden Datensätze durch Folgen getrennt, die aus einem <newline>Plus von einer oder mehreren Leerzeilen bestehen. Führende oder nachfolgende Leerzeilen dürfen zu keinen leeren Datensätzen am Anfang oder Ende der Eingabe führen, und a <newline>muss immer ein Feldtrennzeichen sein. egal wie hoch der Wert von FS ist.

Die Ausgabe-Absätze werden nicht getrennt, da das Ausgabe-Trennzeichen eine einzelne neue Zeile bleibt. Stellen Sie das Trennzeichen für Ausgabedatensätze auf zwei neue Zeilen ein, um sicherzustellen, dass zwischen den Ausgabeabschnitten eine leere Zeile steht:

awk -v RS='' -v ORS='\n\n' '/42B/' file

1
+1 für eine elegante Lösung. Sie müssen die Datei jedoch nicht umleiten ...
jasonwryan

Finger waren auf Autopilot.
llua

2
@jasonwryan, es sei denn, Sie benötigen Zugriff auf den Dateinamen in awk ( FILENAME), ist es keine schlechte Idee, die Umleitung zu verwenden, da dies Probleme mit Dateinamen vermeidet, die enthalten =oder mit -(oder sein -) beginnen, für konsistente Fehlermeldungen sorgt und das Ausführen awkoder Ausführen vermeidet andere Umleitungen, wenn die Eingabedatei nicht geöffnet werden kann.
Stéphane Chazelas

14

Angenommen, die Daten sind so strukturiert, dass es immer die Zeile davor und danach ist. Dann können Sie die Schalter grep -A(after) und -B(before) verwenden, um anzugeben, dass die Zeile 1 vor dem Match und die Zeile 1 danach enthalten soll:

$ grep -A 1 -B 1 "42B" sample.txt
Pseudo name=Apple
Code=42B
state=fault

Wenn Sie vor und nach dem Suchbegriff die gleichen Zahlenzeilen haben möchten, können Sie den -C(Kontext-) Schalter verwenden:

$ grep -C 1 "42B" sample.txt
Pseudo name=Apple
Code=42B
state=fault

Wenn Sie beim Abgleichen mehrerer Zeilen strenger vorgehen möchten, können Sie das Tool verwenden pcregrep, um ein Muster über mehrere Zeilen hinweg abzugleichen:

$ pcregrep -M 'Pseudo.*\n.*42B.*\nstate.*' sample.txt
Pseudo name=Apple
Code=42B
state=fault

Das obige Muster stimmt wie folgt überein:

  • -M - mehrere Zeilen
  • 'Pseudo.*\n.*42B.*\nstate.*'- Stimmt mit einer Gruppe von Zeichenfolgen überein, bei der die erste Zeichenfolge mit dem Wort beginnt, "Pseudo"gefolgt von beliebigen Zeichen bis zum Zeilenende \n, gefolgt von beliebigen Zeichen bis zur Zeichenfolge, "42B"gefolgt von beliebigen Zeichen bis zum anderen Zeilenende ( \n), gefolgt von der Zeichenfolge "state"gefolgt von beliebigen Zeichen.

5
-C(context) kann als Verknüpfung verwendet werden, wenn -Aund gleich -Bsind.
David Baggerman

@ DavidBaggerman - danke. Fügte es der Antwort hinzu.
SLM

Warum stimmen die einen ab? Dies beantwortet die Frage.
SLM

4

Es gibt wahrscheinlich einen ähnlich einfachen Weg, dies mit awk zu tun, aber in Perl:

cat file | perl -ne 'BEGIN { $/="\n\n" }; print if $_ =~ /42B/;'

Das bedeutet im Grunde, dass Sie die Datei in durch Leerzeilen begrenzte Abschnitte aufteilen und dann nur die Abschnitte drucken müssen, die Ihrem regulären Ausdruck entsprechen.


10
Dies kann vereinfacht werden, indem Optionen und Abkürzungen verwendet werden und die nutzlose Verwendung voncat ; perl -00 -ne 'print if /42B/' file
Tripleee

4

Die grepvon einigen Unix-Varianten haben die -pKennzeichnung für "Absatz". Ich weiß, dass AIX es tut .

grep -p 42B <myfile>

würde genau das tun, wonach du fragst. YMMV und GNU grep haben dieses Flag nicht.


Es wäre wunderbar, die -p-Flagge zu haben. Insbesondere, wenn zusammen mit -v verwendet, können Sie ganze Absätze von der Ausgabe ausschließen.
IllvilJa

2

Eine andere Perl-Lösung ohne nachgestellte Leerzeile:

perl -00ne 'if ($_ =~ /42B/) {chomp($_); printf "%s\n",$_}' foo

Beispiel

% perl -00ne 'if ($_ =~ /42B/) {chomp($_); printf "%s\n",$_}' foo
Pseudo name=Apple
Code=42B
state=fault

% cat foo
Pseudo name=Apple
Code=42B
state=fault

Pseudo name=Prance
Code=43B
state=good

1
Oder kürzer (und damit besser lesbar), wie TripleE in einem Kommentar schrieb: perl -00 -ne 'print if /42B/' file.
Mivk
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.