Wie kann ich ein Verzeichnis anhand des Inhalts zweier aufeinanderfolgender Zeilen durchsuchen?

11

Wie kann ich ein Verzeichnis nach Zeilen durchsuchen , die "Foo" enthalten, aber nur Übereinstimmungen erhalten, wenn die nächste Zeile auch "Bar" enthält?

Nathan Long
quelle
Das Problem ist jetzt völlig anders als beim Original: / Vielleicht ist es besser, die alten Versionen zurückzusetzen und eine andere zu POSTEN? Außerdem ist mir die neue Frage nicht klar.
Gilles Quenot
@sputnick - wie so? Ich habe ein Verzeichnis angegeben, als ich die Frage zum ersten Mal gestellt habe. Ich habe es nur gewagt, weil die Leute es nicht bemerkt haben.
Nathan Long
Egal, das wird funktionieren, ich werde meinen POST entsprechend bearbeiten.
Gilles Quenot

Antworten:

7

@ warl0ck zeigte mir die richtige Richtung mit pcregrep, aber ich sagte "enthält", nicht "ist", und ich fragte nach einem Verzeichnis, nicht nach einer Datei.

Das scheint bei mir zu funktionieren.

pcregrep -rMi 'Foo(.*)\n(.*)Bar' .
Nathan Long
quelle
6

Grep selbst scheint es nicht zu unterstützen, verwenden Sie stattdessen pcregrep:

Foo
Bar
Foo
abc

pcregrep -M "Foo\nBar" file

Bekam:

Foo
Bar
Gänseblümchen
quelle
3
Das OP sagte das nicht Foound Barwürde die gesamte Linie umfassen.
Tobrobinson
6

Mit einem sedSkript:

#!/bin/sed -nf

/^Foo/{
    h         # put the matching line in the hold buffer
    n         # going to nextline
    /^Bar/{   # matching pattern in newline
        H     # add the line to the hold buffer
        x     # return the entire paragraph into the pattern space
        p     # print the pattern space
        q     # quit the script now
    }
}

Um es zu benutzen :

chmod +x script.sed
printf '%s\n' * | ./script.sed

Das printfhier , um alle Dateien in einer Zeile jedem, im aktuellen Verzeichnis angezeigt und übergibt es an sed.

Hinweis : Dies ist nach alphabetischer Reihenfolge sortiert.

Weitere Infos von nützlich pattern spaceund hold space HIER .

grymoire.com hat wirklich gute Sachen zum shellProgrammieren.

Gilles Quenot
quelle
Was h, n, H, x, p, qbedeutet das? Sehr interessant.
Yamaneko
Siehe meine Kommentare. Weitere Infos unter pattern space& hold space: grymoire.com/Unix/Sed.html#uh-56 oder in Französisch commentcamarche.net/faq/9536-sed-introduction-a-sed-part-i
Gilles Quenot
POST angepasst, um an einem Verzeichnis zu arbeiten
Gilles Quenot
4

Wenn Sie grepnur verwenden, können Sie die folgende Pipe erstellen:

grep -A1 'Foo' input_file | grep -B1 'Bar' | grep 'Foo'

Der erste greperhält alle Zeilen, die enthalten Foo, sowie die Zeile nach dem Spiel. Dann erhalten wir Zeilen, die Barsowohl die Zeile als auch die Zeile vor dem Match enthalten, und extrahieren schließlich die Zeilen aus dieser Ausgabe, die enthalten Foo.

EDIT: Wie Manatwork hervorhob , gibt es einige problematische Fälle zu beachten . Obwohl es sich aufgrund der greplinienorientierten Funktionalität um eine interessante Herausforderung handelt, ist jede Lösung wahrscheinlich ein "Hack", und Sie sollten wahrscheinlich etwas verwenden, pcregrepdas für die jeweilige Aufgabe besser geeignet ist.

tojrobinson
quelle
Nett. Ich fragte allerdings nach einem Verzeichnis; das scheint zu funktionieren:find . -name '*.txt' | xargs grep -A1 'Foo' | grep -B1 'Bar'
Nathan Long
Dadurch werden auch Vorkommen mit "Foo" und "Bar" in derselben Zeile aufgelistet.
Manatwork
@manatwork: Zeilen, die "Foo" und "Bar" enthalten, sind "Zeilen, die" Foo "enthalten", was gefragt wurde.
Tobrobinson
1
@tojrobinson, was ist mit dem Teil "aber nur Übereinstimmungen erhalten, wenn die nächste Zeile auch" Bar "enthält"? pastebin.com/Yj8aeCEA
Manatwork
3

Während ich Nathans Lösung mit bevorzuge pcregrep, ist hier die Lösung nur mit grep

grep -o -z -P  'Foo(.*)\n(.*)Bar' file

Erklärung der Optionen:

  • -oNur passendes Teil drucken. Erforderlich, da durch Einfügen von -zdie gesamte Datei ausgedruckt wird (es sei denn, irgendwo ist eine \ 0 vorhanden).
  • -z Behandeln Sie die Eingabe als eine Reihe von Zeilen, die jeweils durch ein Null-Byte (das ASCII-NUL-Zeichen) anstelle einer neuen Zeile abgeschlossen werden.
  • -P Perl-Regex-Syntax

BEARBEITEN: Diese Version druckt ganze übereinstimmende Zeilen aus

    grep -o -P -z  '(.*)Foo(.*)\n(.*)Bar(.*)' file
bbaja42
quelle
1
Cooler Trick was -z. Ein "(. *)" Vor und nach dem gesamten Ausdruck würde dazu führen, dass die gesamten übereinstimmenden Zeilen ausgegeben werden. Derzeit werden Teilzeichenfolgen vor "Foo" und nach "Bar" nicht angezeigt.
Manatwork
1

Mit awk:

awk '/bar/ && prev != "" {print FILENAME ": " prev "\n" FILENAME ": " $0}
     /foo/ {prev=$0; next}
     {prev=""}' file1...

(Allgemeiner Hinweis zur awk-Einschränkung: Beachten Sie, dass einige Dateinamen "=" Zeichen enthalten müssen, ./filenameanstatt sie filenamean awk zu übergeben.)

Stéphane Chazelas
quelle