Il concetto di "Hold space" e "Pattern space" in sed


88

Sono confuso dai due concetti in sed: hold space e pattern space. Qualcuno può aiutarli a spiegarli?

Ecco uno snippet del manuale:

h H    Copy/append pattern space to hold space.
g G    Copy/append hold space to pattern space.

n N    Read/append the next line of input into the pattern space.

Questi sei comandi mi confondono davvero.


4
Provalo tu stesso:echo $'1\n2\n3\n4' | sed -n '1~2h;2~2{p;x;p}'
choroba

4
Non essere confuso, solo non usarli. Per qualcosa di diverso dalle semplici sostituzioni su una singola riga dovresti usare awk, non sed. Tenere gli spazi, gli spazi pattern e il 95% dei costrutti del linguaggio sed sono stati inventati prima di awk quando non c'erano alternative migliori. Sono diventati obsoleti non appena awk è stato inventato a metà degli anni '70 e sono mantenuti in vita solo oggi da persone che amano risolvere i problemi usando la sintassi arcana di seds piuttosto che farlo semplicemente e con cura in awk. Se stai usando più di s, g e p (con -n) in sed, allora quasi certamente stai usando lo strumento sbagliato.
Ed Morton

27
Morton awk funziona con dati strutturati (ogni riga ha la stessa struttura). Sed è pensato per lavorare con dati casuali grezzi. Quindi non puoi semplicemente usare awk invece di sed.
Pithikos

5
Consiglio vivamente la lettura info sed. È molto più dettagliato della semplice pagina man.
Fernando Basso

4
Sono d'accordo con Pithikos. Sono andato lungo il sentiero come Morton e mi sono posto la stessa domanda di Morton. Tuttavia, non potevo ancora licenziare sed così facilmente.
eigenfield

Risposte:


117

Quando sed legge un file riga per riga, la riga che è stata attualmente letta viene inserita nel pattern buffer (pattern space). Il buffer del pattern è 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 da poter catturare qualcosa, salvarlo e riutilizzarlo in seguito quando sed sta elaborando un'altra riga. Non si elabora direttamente lo spazio di attesa, invece, è necessario copiarlo o aggiungerlo allo spazio del pattern se si desidera fare qualcosa con esso. Ad esempio, il comando print pstampa solo lo spazio pattern. Allo stesso modo, sopera sullo spazio del pattern.

Ecco un esempio:

sed -n '1!G;h;$p'

(l'opzione -n ​​sopprime la stampa automatica delle righe)

Ci sono tre comandi qui: 1!G, he $p. 1!Gha un indirizzo, 1(prima riga), ma !significa che il comando verrà eseguito ovunque tranne che sulla prima riga. $pd'altra parte verrà eseguito solo sull'ultima riga. Quindi cosa succede è questo:

  1. la prima riga viene letta e inserita automaticamente nello spazio del motivo
  2. sulla prima riga, il primo comando non viene eseguito; hcopia la prima riga nello spazio di attesa .
  3. ora la seconda riga sostituisce qualunque cosa fosse nello spazio del modello
  4. sulla seconda riga, prima eseguiamo G , aggiungendo il contenuto del buffer di blocco al buffer del pattern, separandolo con una nuova riga. Lo spazio del modello ora contiene la seconda riga, una nuova riga e la prima riga.
  5. Quindi, il hcomando inserisce il contenuto concatenato del buffer del pattern nello spazio di blocco, che ora contiene le righe invertite due e una.
  6. Procediamo alla riga numero tre - vai al punto (3) sopra.

Infine, dopo che l'ultima riga è stata letta e lo spazio di blocco (contenente tutte le righe precedenti in ordine inverso) è stato aggiunto allo spazio del motivo, viene stampato lo spazio del motivo p. Come hai intuito, quanto sopra fa esattamente quello che fa il taccomando: stampa il file al contrario.


3
L'opzione G e h funziona come "taglia e aggiungi" ?? Non sembra un'operazione "copia e aggiungi".
Smile

Cosa viene aggiunto con pattern e mantieni lo spazio quando vengono utilizzati comandi nidificati (parentesi graffe)? '195,210{/add/p}'… È possibile estrarre l'ultima riga di un gruppo di linee coinvolte in uno schema?
Sandburg

17

@ Ed Morton: non sono d'accordo con te qui. Ho trovato sedmolto utile e semplice (una volta che hai approfondito il concetto del pattern e hai tenuto i buffer) trovare un modo elegante per eseguire il grepping su più righe.

Ad esempio, prendiamo un file di testo che ha nomi host e alcune informazioni su ogni host, con un sacco di spazzatura in mezzo che 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 la inforiga corrispondente 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!!

(Nota che Host: foo1appare due volte nell'output.)

Spiegazione:

  1. -n disabilita l'output a meno che non venga stampato esplicitamente
  2. prima corrispondenza, trova e mette la Host:linea in hold buffer (h)
  3. seconda corrispondenza, trova la riga successiva Info:, ma prima scambia (x) la riga corrente nel pattern buffer con hold buffer, e stampa (p) la 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 rapidamente risolto 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.


2
In questo caso però potresti semplicemente usare grep:grep 'Host\|Info'
Pithikos

Se sono presenti due righe di informazioni dopo un determinato host, @JensJenson desidera che entrambe le righe di informazioni siano precedute da una riga di informazioni. Penso che modificherò la risposta di conseguenza. Pithikos, grep non sarà sufficiente allora.
Aaron McDaid

4
@ JensJenson, anche l' awkequivalente del tuo codice sed è piuttosto breve:awk '/Host:/{hold=$0}; /Info/{print hold; print;}' myfile.txt
Aaron McDaid

13

Sebbene la risposta di @ gennaio e l'esempio siano carini, la spiegazione non è stata sufficiente per me. Ho dovuto cercare e imparare molto fino a quando non sono riuscito a capire esattamente come sed -n '1!G;h;$p'funziona. Quindi vorrei approfondire il comando per uno 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 tacfa il comando.

sedlegge riga per riga, quindi vediamo cosa succede nello spazio del patten e nello spazio di attesa su ogni riga. Poiché il hcomando copia il contenuto dello spazio del pattern nello spazio di attesa, 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, $pstampa quello d\nc\nb\na$formattato in

d
c
b
a

Se vuoi vedere lo spazio del pattern per ogni riga, puoi aggiungere un lcomando.

$ 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. Lo spazio di attesa è indicato nel 4 ° tutorial, ma consiglio di guardare tutti i video se non hai familiarità sed.

Anche il documento GNU sed e il tutorial Sed di Bruce Barnett sono ottimi riferimenti.


2
Penso che sarà anche utile menzionare che lo spazio per tutti gli scopi pratici è vuoto a meno che non aggiungiamo qualcosa.
Navata il
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.