Come selezionare le linee tra due modelli?

Ho un file come il seguente e mi piacerebbe stampare le linee tra due modelli dati PAT1 e PAT2 .

 1 2 PAT1 3 - first block 4 PAT2 5 6 PAT1 7 - second block PAT2 8 9 PAT1 10 - third block 

Ho letto Come selezionare le linee tra due pattern di marker che possono verificarsi più volte con awk / sed, ma sono curioso di vedere tutte le possibili combinazioni di questo, o stampando il pattern o meno.

Come posso selezionare le linee tra due modelli?

Stampa le linee tra PAT1 e PAT2

 $ awk '/PAT1/,/PAT2/' file PAT1 3 - first block 4 PAT2 PAT1 7 - second block PAT2 PAT1 10 - third block 

Oppure, usando le variabili:

 awk '/PAT1/{flag=1} flag; /PAT2/{flag=0}' file 

Come funziona?

  • /PAT1/ corrisponde alle righe che hanno questo testo, così come /PAT2/ fa.
  • /PAT1/{flag=1} imposta il flag quando il testo PAT1 viene trovato in una riga.
  • /PAT2/{flag=0} distriggers il flag quando il testo PAT2 viene trovato in una riga.
  • flag è un pattern con l’azione di default, che è di print $0 : se flag è uguale a 1 la linea viene stampata. In questo modo, stamperà tutte quelle linee che si verificano dal momento in PAT1 verifica PAT1 e fino al successivo PAT2 è visto. Questo stamperà anche le linee dall’ultima partita di PAT1 fino alla fine del file.

Stampa le linee tra PAT1 e PAT2 – escludendo PAT1 e PAT2

 $ awk '/PAT1/{flag=1; next} /PAT2/{flag=0} flag' file 3 - first block 4 7 - second block 10 - third block 

Questo usa per saltare la linea che contiene PAT1 al fine di evitare che questo venga stampato.

Questa chiamata a next può essere eliminata awk '/PAT2/{flag=0} flag; /PAT1/{flag=1}' file i blocchi: awk '/PAT2/{flag=0} flag; /PAT1/{flag=1}' file awk '/PAT2/{flag=0} flag; /PAT1/{flag=1}' file .

Stampa le linee tra PAT1 e PAT2, incluso PAT1

 $ awk '/PAT1/{flag=1} /PAT2/{flag=0} flag' file PAT1 3 - first block 4 PAT1 7 - second block PAT1 10 - third block 

Posizionando il flag alla fine, triggers l’azione impostata su PAT1 o PAT2: per stampare su PAT1, non per stampare su PAT2.

Stampa le linee tra PAT1 e PAT2, incluso PAT2

 $ awk 'flag; /PAT1/{flag=1} /PAT2/{flag=0}' file 3 - first block 4 PAT2 7 - second block PAT2 10 - third block 

Posizionando il flag all’inizio, triggers l’azione precedentemente impostata e quindi stampa il pattern di chiusura ma non quello di partenza.

Stampa linee tra PAT1 e PAT2 – escluse le linee dall’ultimo PAT1 alla fine del file se non si verifica nessun altro PAT2

Questo è basato su una soluzione di Ed Morton .

 awk 'flag{ if (/PAT2/) {printf "%s", buf; flag=0; buf=""} else buf = buf $0 ORS } /PAT1/ {flag=1}' file 

Come un solitario:

 $ awk 'flag{ if (/PAT2/){printf "%s", buf; flag=0; buf=""} else buf = buf $0 ORS}; /PAT1/{flag=1}' file 3 - first block 4 7 - second block # note the lack of third block, since no other PAT2 happens after it 

Ciò mantiene tutte le linee selezionate in un buffer che viene popolato dal momento in cui viene trovato PAT1. Quindi, continua a essere riempito con le seguenti linee finché non viene trovato PAT2. A quel punto, stampa il contenuto memorizzato e svuota il buffer.

Che mi dici della classica soluzione sed ?

Stampa le linee tra PAT1 e PAT2

 sed -n '/PAT1/,/PAT2/{/PAT1/!{/PAT2/!p}}' file 

o anche (Grazie Sundeep ):

 sed -n '/PAT1/,/PAT2/{//!p}' 

Quanto sopra esclude i confini della gamma.

Stampa le linee tra PAT1 e PAT2, inclusi PAT1 e PAT2

Quanto segue includerebbe i bordi dell’intervallo, che è ancora più semplice:

 sed -n '/PAT1/,/PAT2/p' file 

Stampa le linee tra PAT1 e PAT2, incluso PAT1

Quanto segue include solo l’inizio della gamma:

 sed -n '/PAT1/,/PAT2/{/PAT2/!p}' file 

Stampa le linee tra PAT1 e PAT2, incluso PAT2

Quanto segue include solo la fine della gamma:

 sed -n '/PAT1/,/PAT2/{/PAT1/!p}' file 

Uso di grep con PCRE (ove disponibile) per stampare indicatori e linee tra i marker :

 $ grep -Pzo "(?s)(PAT1(.*?)(PAT2|\Z))" file PAT1 3 - first block 4 PAT2 PAT1 7 - second block PAT2 PAT1 10 - third block 
  • -P perl-regexp, PCRE. Non in tutte le varianti di grep
  • -z Tratta l’input come un insieme di righe, ognuna terminata da un byte zero anziché una nuova riga
  • -o stampa solo la corrispondenza
  • (?s) DotAll, es. punto trova anche le nuove righe
  • (.*?) non trovato
  • \Z Corrisponde solo alla fine della stringa o prima della fine riga

Stampa le linee tra i marcatori escluso il marcatore finale :

 $ grep -Pzo "(?s)(PAT1(.*?)(?=(\nPAT2|\Z)))" file PAT1 3 - first block 4 PAT1 7 - second block PAT1 10 - third block 
  • (.*?)(?=(\nPAT2|\Z)) trova non concordato con lookahead per \nPAT2 e \Z

Stampa le linee tra i marcatori esclusi i marcatori :

 $ grep -Pzo "(?s)((?< =PAT1\n)(.*?)(?=(\nPAT2|\Z)))" file 3 - first block 4 7 - second block 10 - third block 
  • (?< =PAT1\n) lookbehind positivo per PAT1\n

Stampa le linee tra i marker escludendo il marker iniziale :

 $ grep -Pzo "(?s)((?< =PAT1\n)(.*?)(PAT2|\Z))" file 3 - first block 4 PAT2 7 - second block PAT2 10 - third block 

Ecco un altro approccio

Includi entrambi i modelli (predefinito)

 $ awk '/PAT1/,/PAT2/' file PAT1 3 - first block 4 PAT2 PAT1 7 - second block PAT2 PAT1 10 - third block 

Mascherare entrambi i modelli

 $ awk '/PAT1/,/PAT2/{if(/PAT2|PAT1/) next; print}' file 3 - first block 4 7 - second block 10 - third block 

Pattern di inizio maschera

 $ awk '/PAT1/,/PAT2/{if(/PAT1/) next; print}' file 3 - first block 4 PAT2 7 - second block PAT2 10 - third block 

Maschera fine modello

 $ awk '/PAT1/,/PAT2/{if(/PAT2/) next; print}' file PAT1 3 - first block 4 PAT1 7 - second block PAT1 10 - third block 

Puoi fare ciò che vuoi con sed sopprimendo la normale stampa dello spazio pattern con -n . Ad esempio, per includere i motivi nel risultato che puoi fare:

 $ sed -n '/PAT1/,/PAT2/p' filename PAT1 3 - first block 4 PAT2 PAT1 7 - second block PAT2 PAT1 10 - third block 

Per escludere i pattern e stampare semplicemente ciò che c’è tra di loro:

 $ sed -n '/PAT1/,/PAT2/{/PAT1/{n};/PAT2/{d};p}' filename 3 - first block 4 7 - second block 10 - third block 

Che si rompe come

  • sed -n '/PAT1/,/PAT2/ – individua l’intervallo tra PAT1 e PAT2 e sopprime la stampa;

  • /PAT1/{n}; – se corrisponde a PAT1 passa a n (successivo) linea;

  • /PAT2/{d}; – se corrisponde PAT2 riga di cancellazione di PAT2 ;

  • p – stampa tutte le linee che rientrano in /PAT1/,/PAT2/ e non sono state saltate o cancellate.

In alternativa:

 sed '/START/,/END/!d;//d' 

Questo elimina tutte le righe tranne quelle comprese tra START e END, quindi //d cancella le linee START e END poiché // causa sed all’uso dei pattern precedenti.