Come posso grep una directory in base al contenuto di due righe successive?


11

Come posso grep una directory per le righe che contengono "Foo", ma ottenere corrispondenze solo quando la riga successiva contiene anche "Bar"?


Il problema ora è totalmente diverso dall'originale: / Forse è meglio ripristinare le versioni precedenti e postarne un'altra? Inoltre la nuova domanda non è chiara per me.
Gilles Quenot,

@sputnick - come? Ho specificato una directory quando ho pubblicato la domanda per la prima volta; L'ho osato solo perché la gente non se ne accorgeva.
Nathan Long,

Non importa, funzionerà, modificherò il mio POST di conseguenza.
Gilles Quenot,

Risposte:


7

@ 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' .

6

Grep stesso non sembra supportarlo, usa invece pcregrep:

Foo
Bar
Foo
abc

pcregrep -M "Foo\nBar" file

Avuto:

Foo
Bar

3
L'OP non ha detto questo Fooe Barcomprenderebbe l'intera linea.
Tojrobinson,

6

Con una sedsceneggiatura:

#!/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 printfqui 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 spacee hold space QUI .

grymoire.com ha davvero delle buone cose sulla shellprogrammazione.


Cosa h, n, H, x, p, qsignifica? Molto interessante.
Yamaneko,

Vedi i miei commenti Ulteriori informazioni su pattern space& hold space: grymoire.com/Unix/Sed.html#uh-56 o in francese commentcamarche.net/faq/9536-sed-introduction-a-sed-part-i
Gilles Quenot

POST adattato per lavorare su una directory
Gilles Quenot,

4

Utilizzando grepsolo, è possibile costruire la seguente pipe:

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

Il primo grepotterrà tutte le linee che contengono Fooe la linea dopo la partita. Quindi otteniamo linee che contengono Barcosì 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 grepfunzionalità orientata alla linea, qualsiasi soluzione con essa è probabilmente un 'hack' e probabilmente stai meglio usando qualcosa del genere pcregrepche è più adatto al compito da svolgere.


Bello. Ho chiesto una directory però; questo sembra funzionare:find . -name '*.txt' | xargs grep -A1 'Foo' | grep -B1 'Bar'
Nathan Long

Verranno inoltre elencati gli eventi con "Foo" e "Bar" sulla stessa riga.
arte

@manatwork: Le linee che contengono "Foo" e "Bar" sono "linee che contengono" Foo "" che è stato ciò che è stato chiesto.
Tojrobinson,

1
@tojrobinson, che dire della "ma ottieni corrispondenze solo quando la riga successiva contiene anche la parte" Bar "? pastebin.com/Yj8aeCEA
manatwork

3

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:

  • -ostampa solo la parte corrispondente. Necessario poiché l'inclusione di -zverrà 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

1
Bel trucco cosa -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.
arte

1

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 ./filenameinvece di filenameawk)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.