@Ed Morton:non sono d'accordo con te qui. Ho trovato sed
molto utile e semplice (una volta capito il concetto del pattern e tenuto i buffer) per trovare un modo elegante per eseguire il grepping multilinea.
Ad esempio, prendiamo un file di testo che contiene nomi host e alcune informazioni su ogni host, con un sacco di spazzatura in mezzo di cui non mi interessa.
Host: foo1
some junk, doesnt matter
some junk, doesnt matter
Info: about foo1 that I really care about!!
some junk, doesnt matter
some junk, doesnt matter
Info: a second line about foo1 that I really care about!!
some junk, doesnt matter
some junk, doesnt matter
Host: foo2
some junk, doesnt matter
Info: about foo2 that I really care about!!
some junk, doesnt matter
some junk, doesnt matter
Per me, uno script awk per ottenere solo le righe con il nome host e il corrispondente info
line richiederebbe un po' più di quello che sono in grado di fare con sed:
sed -n '/Host:/{h}; /Info/{x;p;x;p;}' myfile.txt
l'output è simile a:
Host: foo1
Info: about foo1 that I really care about!!
Host: foo1
Info: a second line about foo1 that I really care about!!
Host: foo2
Info: about foo2 that I really care about!!
(Notare che Host: foo1
appare due volte nell'output.)
Spiegazione:
-n
disabilita l'output a meno che non venga stampato esplicitamente- prima corrispondenza, trova e inserisce
Host:
linea nel buffer di attesa (h) - seconda corrispondenza, trova la riga Info:successiva, ma prima scambia (x) la riga corrente nel buffer del pattern con il buffer di attesa e stampa (p) il
Host:
riga, quindi scambia nuovamente (x) e stampa (p) la riga Info:.
Sì, questo è un esempio semplicistico, ma sospetto che questo sia un problema comune che è stato risolto rapidamente da un semplice sed one-liner. Per compiti molto più complessi, come quelli in cui non puoi fare affidamento su una data sequenza prevedibile, awk potrebbe essere più adatto.
Quando sed legge un file riga per riga, la riga che è stata attualmente letta viene inserita nel pattern buffer (spazio modello). Il pattern buffer è come il buffer temporaneo, lo scratchpad in cui sono memorizzate le informazioni correnti. Quando dici a sed di stampare, stampa il pattern buffer.
Hold buffer / hold space è come una memoria a lungo termine, in modo tale che tu possa catturare qualcosa, memorizzarlo e riutilizzarlo in seguito quando sed sta elaborando un'altra riga. Non elabori direttamente lo spazio di attesa, invece, devi copiarlo o aggiungerlo allo spazio del modello se vuoi fare qualcosa con esso. Ad esempio, il comando di stampa p
stampa solo lo spazio pattern. Allo stesso modo, s
opera sullo spazio dei pattern.
Ecco un esempio:
sed -n '1!G;h;$p'
(l'opzione -n sopprime la stampa automatica delle linee)
Ci sono tre comandi qui:1!G
, h
e $p
. 1!G
ha un indirizzo, 1
(prima riga), ma il !
significa che il comando verrà eseguito ovunque ma sulla prima riga. $p
d'altra parte verrà eseguito solo sull'ultima riga. Quindi quello che succede è questo:
- la prima riga viene letta e inserita automaticamente nello spazio del modello
- sulla prima riga, il primo comando non viene eseguito;
h
copia la prima riga nel hold spazio. - ora la seconda riga sostituisce ciò che era nello spazio del modello
- sulla seconda riga, prima eseguiamo
G
, aggiungendo il contenuto del buffer di blocco al buffer del modello, separandolo con una nuova riga. Lo spazio pattern ora contiene la seconda riga, un newline e la prima riga. - Poi,
h
Il comando inserisce i contenuti concatenati del pattern buffer nello spazio di attesa, che ora contiene le righe invertite due e una. - Procediamo alla riga numero tre -- vai al punto (3) sopra.
Infine, dopo che l'ultima riga è stata letta e lo spazio di attesa (contenente tutte le righe precedenti in ordine inverso) è stato aggiunto al pattern space, il pattern space viene stampato con p
. Come hai intuito, quanto sopra fa esattamente ciò che il tac
comando esegue -- stampa il file al contrario.
Sebbene la risposta di @January e l'esempio siano carini, la spiegazione non mi è bastata. Ho dovuto cercare e imparare molto finché non sono riuscito a capire come esattamente sed -n '1!G;h;$p'
lavori. Quindi mi piacerebbe approfondire il comando per qualcuno come me.
Prima di tutto, vediamo cosa fa il comando.
$ echo {a..d} | tr ' ' '\n' # Prints from 'a' to 'd' in each line
a
b
c
d
$ echo {a..d} | tr ' ' '\n' | sed -n '1!G;h;$p'
d
c
b
a
Inverte l'input come tac
comando lo fa.
sed
si legge riga per riga, quindi vediamo cosa succede nello patten space e lo spazio di attesa ad ogni riga. Come h
Il comando copia il contenuto dello spazio pattern nello spazio hold, entrambi gli spazi hanno lo stesso testo.
Read line Pattern Space / Hold Space Command executed
-----------------------------------------------------------
a a$ h
b b\na$ 1!G;h
c c\nb\na$ 1!G;h
d d\nc\nb\na$ 1!G;h;$p
Nell'ultima riga, $p
stampa d\nc\nb\na$
che è formattato in
d
c
b
a
Se vuoi vedere lo spazio del modello per ogni riga, puoi aggiungere un l
comando.
$ echo {a..d} | tr ' ' '\n' | sed -n '1!G;h;l;$p'
a$
b\na$
c\nb\na$
d\nc\nb\na$
d
c
b
a
Ho trovato molto utile guardare questo video tutorial Capire come funziona sed, poiché il ragazzo mostra come ogni spazio verrà utilizzato passo dopo passo. La presa distanziata è citata nel 4° tutorial, ma ti consiglio di guardare tutti i video se non hai familiarità con sed
.
Anche il documento GNU sed e il tutorial Sed di Bruce Barnett sono ottimi riferimenti.