Come selezionare le linee tra due pattern marker che possono verificarsi più volte con awk / sed


119

Utilizzando awko sedcome posso selezionare le linee che si verificano tra due diversi modelli di marker? Potrebbero esserci più sezioni contrassegnate con questi modelli.

Ad esempio: supponiamo che il file contenga:

abc
def1
ghi1
jkl1
mno
abc
def2
ghi2
jkl2
mno
pqr
stu

E lo schema iniziale è abce lo schema finale è mno Quindi, ho bisogno dell'output come:

def1
ghi1
jkl1
def2
ghi2
jkl2

Sto usando sed per abbinare il pattern una volta:

sed -e '1,/abc/d' -e '/mno/,$d' <FILE>

C'è un modo sedo awk per farlo ripetutamente fino alla fine del file?

Risposte:


188

Utilizzare awkcon una bandiera per attivare la stampa quando necessario:

$ awk '/abc/{flag=1;next}/mno/{flag=0}flag' file
def1
ghi1
jkl1
def2
ghi2
jkl2

Come funziona?

  • /abc/corrisponde alle righe con questo testo, così come /mno/fa.
  • /abc/{flag=1;next}imposta flagquando il testo abcviene trovato. Quindi, salta la linea.
  • /mno/{flag=0}ripristina flagquando il testo mnoviene trovato.
  • Il finale flagè un pattern con l'azione predefinita, che è print $0: se flagè uguale a 1 viene stampata la linea.

Per una descrizione più dettagliata ed esempi, insieme ai casi in cui i modelli vengono visualizzati o meno, vedere Come selezionare le linee tra due modelli? .


30
Se vuoi stampare tutto ciò che si trova tra e incluso il motivo, puoi usare awk '/abc/{a=1}/mno/{print;a=0}a' file.
scai

6
Sì, @scai! o anche awk '/abc/{a=1} a; /mno/{a=0}' file- con questo, mettendo acondition prima di, /mno/facciamo in modo che valuti la linea come vera (e la stampiamo) prima di impostare a=0. In questo modo possiamo evitare di scrivere print.
fedorqui 'SO stop harming'

12
@scai @fedorqui Per includere l'output del pattern, puoi farloawk '/abc/,/mno/' file
Jotne

1
@hkasera awk '/abc/{flag=1}/mno/{flag=0}flag' filedovrebbe fare.
fedorqui 'SO stop harming'

2
@EirNym questo è uno scenario strano che può essere gestito in modi molto diversi: quali righe vorresti stampare? Probabilmente l' awk 'flag; /PAT1/{flag=1; next} /PAT1/{flag=0}' fileavrebbe fatto.
fedorqui 'SO smettila di nuocere'

45

Utilizzando sed:

sed -n -e '/^abc$/,/^mno$/{ /^abc$/d; /^mno$/d; p; }'

L' -nopzione significa non stampare per impostazione predefinita.

Il pattern cerca le righe che contengono solo abcto just mno, quindi esegue le azioni nel file { ... }. La prima azione cancella la abcriga; la seconda la mnolinea; e pstampa le righe rimanenti. Puoi rilassare le espressioni regolari come richiesto. Qualsiasi riga al di fuori dell'intervallo di abc.. mnosemplicemente non viene stampata.


Grazie per la risposta e per la spiegazione! :)
dvai

@JonathanLeffler posso sapere qual è lo scopo dell'utilizzo-e
Kasun Siyambalapitiya

1
@KasunSiyambalapitiya: Principalmente significa che mi piace usarlo. Formalmente, specifica che l'argomento successivo è (parte dello) script che seddovrebbe essere eseguito. Se vuoi o hai bisogno di usare diversi argomenti per includere l'intero script, allora devi usare -eprima di ciascuno di questi argomenti; in caso contrario, è facoltativo (ma esplicito).
Jonathan Leffler

@JonathanLeffler Thanks
Kasun Siyambalapitiya

Bello! (Preferisco sed su awk.) Quando si usano espressioni regolari complesse, sarebbe bello non doverle ripetere. Non è possibile eliminare la prima / ultima riga dell'intervallo "selezionato"? O applicare prima il da tutte le righe fino alla prima corrispondenza e poi un altro da tutte le righe che iniziano con la seconda corrispondenza?
hans_meine

18

Questo potrebbe funzionare per te (GNU sed):

sed '/^abc$/,/^mno$/{//!b};d' file

Elimina tutte le righe tranne quelle comprese tra le righe che iniziano abcemno



Questo e spettacolare. Le {//!b}impedisce la abce mnovengano inclusi nell'output, ma non riescono a capire come. Potresti spiegare?
Brendan

1
@Brendan l'istruzione //!blegge se la riga corrente non è nessuna delle righe che corrispondono all'intervallo, spezza e quindi stampa quelle righe altrimenti tutte le altre righe vengono eliminate.
potong

13
sed '/^abc$/,/^mno$/!d;//d' file

gioca a golf due personaggi meglio di ppotong {//!b};d

Le barre vuote //significano: "riutilizza l'ultima espressione regolare usata". e il comando fa lo stesso del più comprensibile:

sed '/^abc$/,/^mno$/!d;/^abc$/d;/^mno$/d' file

Questo sembra essere POSIX :

Se una RE è vuota (cioè, non è specificato alcun pattern) sed si comporterà come se fosse stata specificata l'ultima RE usata nell'ultimo comando applicato (sia come indirizzo che come parte di un comando sostitutivo).


1
Penso che la seconda soluzione non finirà con nulla poiché anche il secondo comando è un intervallo. Comunque complimenti per il primo.
potong

@potong true! Devo studiare di più perché il primo funziona. Grazie!
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

7

Dai link della risposta precedente, quello che lo ha fatto per me, in esecuzione kshsu Solaris, era questo:

sed '1,/firstmatch/d;/secondmatch/,$d'
  • 1,/firstmatch/d: dalla riga 1 fino alla prima volta che trovi firstmatch, elimina.
  • /secondmatch/,$d: dalla prima occorrenza secondmatchfino alla fine del file, elimina.
  • Il punto e virgola separa i due comandi, che vengono eseguiti in sequenza.

Solo curioso, perché il limitatore di gamma ( 1,) viene prima /firstmatch/? Immagino che questo potrebbe anche essere formulato '/firstmatch/1,d;/secondmatch,$d'?
Luke Davis

2
Con "1, / firstmatch / d" stai dicendo "dalla riga 1 fino alla prima volta che trovi 'firstmatch', cancella". Mentre, con "/ secondmatch /, $ d" dici "dalla prima occorrenza di 'secondmatch' fino alla fine del file, cancella". il punto e virgola separa i due comandi, che vengono eseguiti in sequenza.
FanDeLaU

2
perl -lne 'print if((/abc/../mno/) && !(/abc/||/mno/))' your_file

Buono a sapersi perl equivalente in quanto è una buona alternativa sia ad awk che a sed.
akhan

2

qualcosa del genere funziona per me:

file.awk:

BEGIN {
    record=0
}

/^abc$/ {
    record=1
}

/^mno$/ {
    record=0;
    print "s="s;
    s=""
}

!/^abc|mno$/ {
    if (record==1) {
        s = s"\n"$0
    }   
}

utilizzando: awk -f file.awk data...

modifica: la soluzione O_o fedorqui è decisamente migliore / più carina della mia.


3
In GNU awk if (record=1)dovrebbe essere if (record==1), cioè double = - vedi operatori di confronto gawk
George Hawkins,

2

La risposta di Don_crissti da Mostra solo testo tra 2 modelli corrispondenti ?

firstmatch="abc"
secondmatch="cdf"
sed "/$firstmatch/,/$secondmatch/!d;//d" infile

che è molto più efficiente dell'applicazione di AWK, vedi qui .


Non penso che collegare i confronti temporali qui abbia molto senso, poiché i requisiti delle domande sono molto diversi, da qui le soluzioni.
fedorqui 'SO smettila di nuocere'

2
Non sono d'accordo perché dovremmo avere alcuni criteri per confrontare le risposte. Solo pochi hanno applicazioni SED.
Léo Léopold Hertz 준영

0

Ho provato a usare awkper stampare le linee tra due modelli mentre pattern2 corrisponde anche a pattern1 . E dovrebbe essere stampata anche la riga pattern1.

ad es. sorgente

package AAA
aaa
bbb
ccc
package BBB
ddd
eee
package CCC
fff
ggg
hhh
iii
package DDD
jjj

dovrebbe avere un'uscita di

package BBB
ddd
eee

Dove pattern1 è package BBB, pattern2 è package \w*. Nota che CCCnon è un valore noto, quindi non può essere letteralmente abbinato.

In questo caso, né @scai awk '/abc/{a=1}/mno/{print;a=0}a' filené @fedorqui awk '/abc/{a=1} a; /mno/{a=0}' filefunzionano per me.

Alla fine, sono riuscito a risolverlo awk '/package BBB/{flag=1;print;next}/package \w*/{flag=0}flag' file, haha

Un po 'più di sforzo si traduce in awk '/package BBB/{flag=1;print;next}flag;/package \w*/{flag=0}' file, per stampare anche la linea pattern2, cioè,

package BBB
ddd
eee
package CCC
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.