Wie kann ich ein Verzeichnis nach Zeilen durchsuchen , die "Foo" enthalten, aber nur Übereinstimmungen erhalten, wenn die nächste Zeile auch "Bar" enthält?
Wie kann ich ein Verzeichnis nach Zeilen durchsuchen , die "Foo" enthalten, aber nur Übereinstimmungen erhalten, wenn die nächste Zeile auch "Bar" enthält?
Antworten:
@ 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' .
Grep selbst scheint es nicht zu unterstützen, verwenden Sie stattdessen pcregrep:
Foo
Bar
Foo
abc
pcregrep -M "Foo\nBar" file
Bekam:
Foo
Bar
Foo
und Bar
würde die gesamte Linie umfassen.
Mit einem sed
Skript:
#!/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 printf
hier , 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 space
und hold space
HIER .
grymoire.com hat wirklich gute Sachen zum shell
Programmieren.
h, n, H, x, p, q
bedeutet das? Sehr interessant.
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
Wenn Sie grep
nur verwenden, können Sie die folgende Pipe erstellen:
grep -A1 'Foo' input_file | grep -B1 'Bar' | grep 'Foo'
Der erste grep
erhält alle Zeilen, die enthalten Foo
, sowie die Zeile nach dem Spiel. Dann erhalten wir Zeilen, die Bar
sowohl 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 grep
linienorientierten Funktionalität um eine interessante Herausforderung handelt, ist jede Lösung wahrscheinlich ein "Hack", und Sie sollten wahrscheinlich etwas verwenden, pcregrep
das für die jeweilige Aufgabe besser geeignet ist.
find . -name '*.txt' | xargs grep -A1 'Foo' | grep -B1 'Bar'
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:
-o
Nur passendes Teil drucken. Erforderlich, da durch Einfügen von -z
die 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
-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.
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, ./filename
anstatt sie filename
an awk zu übergeben.)