Come posso grep una directory per le righe che contengono "Foo", ma ottenere corrispondenze solo quando la riga successiva contiene anche "Bar"?
Come posso grep una directory per le righe che contengono "Foo", ma ottenere corrispondenze solo quando la riga successiva contiene anche "Bar"?
Risposte:
@ warl0ck mi ha indicato nella giusta direzione pcregrep
, ma ho detto "contiene", non "è" e ho chiesto informazioni su una directory, non su un file.
Questo sembra funzionare per me.
pcregrep -rMi 'Foo(.*)\n(.*)Bar' .
Grep stesso non sembra supportarlo, usa invece pcregrep:
Foo
Bar
Foo
abc
pcregrep -M "Foo\nBar" file
Avuto:
Foo
Bar
Foo
e Bar
comprenderebbe l'intera linea.
Con una sed
sceneggiatura:
#!/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
}
}
Per usarlo :
chmod +x script.sed
printf '%s\n' * | ./script.sed
Il printf
qui visualizzare tutti i file nella directory corrente su una linea ciascuno, e passarlo al sed
.
Nota : questo è ordinato in ordine alfabetico.
Altre informazioni utili pattern space
e hold space
QUI .
grymoire.com ha davvero delle buone cose sulla shell
programmazione.
h, n, H, x, p, q
significa? Molto interessante.
pattern space
& hold space
: grymoire.com/Unix/Sed.html#uh-56 o in francese commentcamarche.net/faq/9536-sed-introduction-a-sed-part-i
Utilizzando grep
solo, è possibile costruire la seguente pipe:
grep -A1 'Foo' input_file | grep -B1 'Bar' | grep 'Foo'
Il primo grep
otterrà tutte le linee che contengono Foo
e la linea dopo la partita. Quindi otteniamo linee che contengono Bar
così come la linea prima della corrispondenza e infine estraiamo le linee da questo output che contengono Foo
.
EDIT: Come ha sottolineato il lavoro manuale , ci sono alcuni casi problematici di cui osservare. Sebbene sia una sfida interessante, a causa della grep
funzionalità orientata alla linea, qualsiasi soluzione con essa è probabilmente un 'hack' e probabilmente stai meglio usando qualcosa del genere pcregrep
che è più adatto al compito da svolgere.
find . -name '*.txt' | xargs grep -A1 'Foo' | grep -B1 'Bar'
Mentre preferisco usare la soluzione di Nathan pcregrep
, ecco una soluzione che usa solo grep
grep -o -z -P 'Foo(.*)\n(.*)Bar' file
Spiegazione delle opzioni:
-o
stampa solo la parte corrispondente. Necessario poiché l'inclusione di -z
verrà stampato l'intero file (a meno che non sia presente un \ 0 da qualche parte)-z
Considera l'input come un insieme di linee, ciascuna terminata da un byte zero (il carattere ASCII NUL) anziché da una nuova riga.-P
sintassi perge regex EDIT: questa versione stampa intere linee abbinate
grep -o -P -z '(.*)Foo(.*)\n(.*)Bar(.*)' file
-z
. Alcuni "(. *)" Prima e dopo l'intera espressione renderebbero in output tutte le linee corrispondenti. Per ora le sottostringhe prima di "Foo" e dopo "Bar" non vengono visualizzate.
Con awk:
awk '/bar/ && prev != "" {print FILENAME ": " prev "\n" FILENAME ": " $0}
/foo/ {prev=$0; next}
{prev=""}' file1...
(nota generale sulla limitazione di awk: attenzione che se alcuni nomi di file possono contenere caratteri "=", dovrai passarli come ./filename
invece di filename
awk)