Come posso grep per più pattern su più linee?


Risposte:


14

Aggiornato il 18-nov-2016 (poiché il comportamento di grep è cambiato: grep con il parametro -P ora non supporta ^e $ancore [su Ubuntu 16.04 con kernel v: 4.4.0-21-generico]) ( correzione errata (non) )

$ grep -Pzo "begin(.|\n)*\nend" file
begin
Some text goes here.  
end

nota: per altri comandi basta sostituire le ancore '^' & '$' con l'ancora di nuova linea '\n' ______________________________

Con il comando grep:

grep -Pzo "^begin\$(.|\n)*^end$" file

Se vuoi non includere i modelli "inizio" e "fine" nel risultato, usa grep con il supporto di Lookbehind e Lookahead.

grep -Pzo "(?<=^begin$\n)(.|\n)*(?=\n^end$)" file

Inoltre puoi utilizzare la \Knotifica invece dell'asserzione Lookbehind.

grep -Pzo "^begin$\n\K(.|\n)*(?=\n^end$)" file

\KL'opzione ignora tutto prima della corrispondenza del modello e ignora il modello stesso.
\nusato per evitare di stampare righe vuote dall'output.

O come suggerisce @AvinashRaj ci sono semplici semplici grep come segue:

grep -Pzo "(?s)^begin$.*?^end$" file

grep -Pzo "^begin\$[\s\S]*?^end$" file

(?s)dice a grep di consentire al punto di abbinare i caratteri di nuova riga.
[\s\S]corrisponde a qualsiasi personaggio che sia spazio bianco o non spazio bianco.

E il loro output senza includere "inizio" e "fine" è il seguente:

grep -Pzo "^begin$\n\K[\s\S]*?(?=\n^end$)" file # or grep -Pzo "(?<=^begin$\n)[\s\S]*?(?=\n^end$)"

grep -Pzo "(?s)(?<=^begin$\n).*?(?=\n^end$)" file

vedere qui il test completo di tutti i comandi ( non aggiornato poiché il comportamento grep con il parametro -P è cambiato )

Nota:

^punta l'inizio di una linea e $punta la fine di una linea. questi sono stati aggiunti al punto "inizio" e "fine" per abbinarli se sono soli in una riga.
In due comandi sono scappato $perché utilizzava anche "Sostituzione comando" ( $(command)) che consente all'output di un comando di sostituire il nome del comando.

Da man grep:

-o, --only-matching
      Print only the matched (non-empty) parts of a matching line,
      with each such part on a separate output line.

-P, --perl-regexp
      Interpret PATTERN as a Perl compatible regular expression (PCRE)

-z, --null-data
      Treat the input as a set of lines, each terminated by a zero byte (the ASCII 
      NUL character) instead of a newline. Like the -Z or --null option, this option 
      can be used with commands like sort -z to process arbitrary file names.

cambia il tuo grep come se grep -Pzo "(?<=begin\n)(.|\n)*(?=\nend)" filenon \niniziasse a stampare il carattere esistente sulla riga.
Avinash Raj,

Usa il modificatore DOTALL per creare punti in modo che corrispondano anche ai caratteri di nuova rigagrep -Pzo "(?s)begin.*?end" file
Avinash Raj,

O semplicemente,grep -Pzo "begin[\s\S]*?end" file
Avinash Raj,

1
La siólution non funziona. Produce un errore: grep: ein nicht geschütztes ^ oder $ wird mit -Pz nicht unterstütztla traduzione dell'errore è simile a:grep: a not protected ^ or $ is not supported with -Pz
musbach,

1
Sì, lo so, è nella tua risposta. Sono sicuro che ha funzionato quando hai pubblicato questo, ma riprovalo oggi. Il comportamento di grepsembra essere cambiato.
terdon,

2

Nel caso in cui il tuo grepnon supporti la sintassi perl ( -P), puoi provare a unire le linee, abbinando il modello, quindi espandendo di nuovo le linee come di seguito:

$ tr '\n' , < foo.txt | grep -o "begin.*end" | tr , '\n'
begin
Some text goes here.
end
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.