Come posso cercare un modello multilinea in un file?


128

Avevo bisogno di trovare tutti i file che contenevano uno schema di stringhe specifico. La prima soluzione che viene in mente sta usando find piped con xargs grep :

find . -iname '*.py' | xargs grep -e 'YOUR_PATTERN'

Ma se devo trovare schemi che si estendono su più di una riga, sono bloccato perché greilla vaniglia non riesce a trovare schemi multilinea.



2
Questo è più vecchio, quindi direi che non è un duplicato :)
rogerdpack il

@rogerdpack Quando si contrassegnano le domande come duplicati, l'età di una domanda è un problema terziario, dopo la quantità e la qualità delle risposte e la qualità della domanda.
triplo il

Risposte:


98

Così ho scoperto pcregrep che è l'acronimo di Perl Compatible Regular Expressions GREP .

Ad esempio, è necessario trovare i file in cui la variabile ' _name ' è immediatamente seguita dalla variabile ' _description ':

find . -iname '*.py' | xargs pcregrep -M '_name.*\n.*_description'

Suggerimento: è necessario includere il carattere di interruzione di riga nel modello. A seconda della piattaforma, potrebbe essere '\ n', \ r ',' \ r \ n ', ...


7
Come menzionato da Halka di seguito, "puoi anche persuadere il carattere jolly punto per abbinare le nuove linee se aggiungi (? S) alla tua espressione regolare". Quindi usa grep con perl regex aggiungendo -P. trova . -exec grep -nHP '(? s) SELECT. {1,60} FROM. {1,20} nome_tabella' '{}' \;
Jim,

8
pcregrepè disponibile sul Mac conbrew install pcre
Jared Beck,

1
Ancora meglio: anche utilizzare -Hche stampa il nome del file prima di ogni partita: pcregrep -HM.
Ciro Santilli 21 冠状 病 六四 事件 法轮功

97

Perché non vai per Awk :

awk '/Start pattern/,/End pattern/' filename

2
Questo è molto più facile da capire e da usare awkche viene fornito con la maggior parte dei sistemi * nix.
Ali Karbassi,

24
simpatico! c'è un modo per rendere questa partita non golosa?
marcin

3
Come stamperesti il ​​nome del file solo quando c'è una corrispondenza?
bibstha,

2
Puoi mostrare i numeri di riga delle partite con awk '/Start pattern/,/End pattern/ {printf NR " "; print}' filename. Si può rendere più bella, dando i numeri di riga una larghezza fissa: awk '/Start pattern/,/End pattern/ {printf "%-4s ", NR; print}' filename.
Robert,

Questo sembra funzionare bene su un singolo file, tuttavia, se mi piacerebbe cercare tra più file?
Jinstrong,

84

Ecco l'esempio usando GNUgrep :

grep -Pzo '_name.*\n.*_description'

-z/ --null-dataTratta i dati di input e output come sequenze di linee.

Vedi anche qui


1
Questo spiega solo un singolo personaggio di nuova linea, credo.
Cloud

1
Non sono stato in grado di utilizzare grep per la -zricerca su più righe, senza usare i flag, quindi non divide la ricerca su una sola riga e -oper stampare solo la parte corrispondente.
bbaja42,

Ho scoperto che -o ha fatto sì che non stampasse nulla, ma -l ha funzionato per ottenere un elenco di file (il mio comando era grep -rzl pattern *, -rzo non ha funzionato)
Benubird

5
Consiglio '' grep -Pazo '' invece di '' -Pzo '' per file non ASCII. È meglio perché l'opzione -z su file non ASCII può innescare il comportamento dei "dati binari" di grep che modifica i valori di ritorno. Cambia '' -a | --text '' impedisce questo.
rloth

Non funziona su Mac con git installato dabrew reinstall --with-pcre git
Quanlong il

21

grep -Pusa anche libpcre, ma è molto più ampiamente installato. Per trovare una titlesezione completa di un documento html, anche se si estende su più righe, è possibile utilizzare questo:

grep -P '(?s)<title>.*</title>' example.html

Poiché il progetto PCRE si attua allo standard perl, utilizzare la documentazione perl come riferimento:


Hmm ci ha provato proprio ora e sembra non funzionare ... gist.github.com/rdp/0286d91624930bd11d0169d6a6337c33
rogerdpack

Non sapevo che grep avesse questa opzione. Probabilmente per questo: questo è altamente sperimentale e grep -P può avvertire di funzionalità non implementate. ; questo è sotto CentOS 7. Sotto Fedora 29: Questo è sperimentale e grep -P può avvertire di funzionalità non implementate . Ovviamente in BSD grep non c'è affatto. Sarebbe bello se non fosse così sperimentale ma è bello ricordarselo, anche se probabilmente lo userò.
Pryftan,

17

Ecco un esempio più utile:

pcregrep -Mi "<title>(.*\n){0,5}</title>" afile.html

Cerca il tag del titolo in un file html anche se si estende su 5 righe.

Ecco un esempio di linee illimitate:

pcregrep -Mi "(?s)<title>.*</title>" example.html 

4
grazie per questo. Ero bloccato non rendendomi conto che un carattere jolly non corrisponderebbe al personaggio della nuova riga.
opaco

7
@matt: puoi anche persuadere il carattere jolly punto per abbinare le nuove righe se aggiungi (?s)alla tua espressione regolare, in questo modo:"(?s)<html>.*</html>"
lubomir.brindza

@matt Ovviamente puoi cercare $(alla fine di un modello) per indicare che è la fine della linea, anche se non è la stessa cosa che ti aiuta a trovare più motivi di linea. Vedi anche glob(7). È inoltre possibile trovare questo sito Web di interesse: regular-expressions.info
Pryftan


4

È possibile utilizzare il setaccio alternativo grep qui (dichiarazione di non responsabilità: sono l'autore).

Supporta la corrispondenza multilinea e la limitazione della ricerca a tipi di file specifici pronti all'uso:

setaccia -m --files '* .py' 'YOUR_PATTERN'

(cerca tutti i file * .py per il modello regex multilinea specificato)

È disponibile per tutti i principali sistemi operativi. Dai un'occhiata alla pagina degli esempi per vedere come può essere usato per estrarre valori multilinea da un file XML.


3

Questa risposta potrebbe essere utile:

È necessario Regex (grep) per la ricerca su più righe

Per trovare ricorsivamente puoi usare i flag -R (ricorsivo) e --include (modello GLOB). Vedere:

Usa grep --exclude / - include la sintassi per non grep attraverso determinati file


@ Ɖiamond ǤeezeƦ nota che la modifica di un post in LQP ( stackoverflow.com/review/low-quality-posts/19341146 ) invalida la recensione, quindi modifica se sei sicuro che il post debba essere mantenuto.
fedorqui "SO smettere di danneggiare" il

2

@Marcin: esempio awk non avido:

awk '{if ($0 ~ /Start pattern/) {triggered=1;}if (triggered) {print; if ($0 ~ /End pattern/) { exit;}}}' filename

2
perl -ne 'print if (/begin pattern/../end pattern/)' filename

Questo stampa l'intero file però
Herbert

1

Utilizzo dell'opzioneex / vieditor e globstar (sintassi simile a awke sed):

ex +"/string1/,/string3/p" -R -scq! file.txt

dov'è il aaatuo punto di partenza ed bbbè il tuo testo finale.

Per cercare ricorsivamente, prova:

ex +"/aaa/,/bbb/p" -scq! **/*.py

Nota: per abilitare la **sintassi, eseguire shopt -s globstar(Bash 4 o zsh).

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.