El concepto de 'Espacio de retención' y 'Espacio de patrón' en sed

86

Estoy confundido por los dos conceptos en sed: mantener el espacio y el espacio del patrón. ¿Alguien puede ayudar a explicarlos?

Aquí hay un fragmento del manual:

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.

Estos seis comandos realmente me confunden.

ChenQi
fuente
4
Pruébelo usted mismo:echo $'1\n2\n3\n4' | sed -n '1~2h;2~2{p;x;p}'
choroba
4
No se confunda, simplemente no los use. Para cualquier otra cosa que no sean sustituciones simples en una sola línea, debe usar awk, no sed. Los espacios de retención, los espacios de patrones y el 95% de las construcciones del lenguaje sed se inventaron antes de awk cuando no había una alternativa mejor. Se volvieron obsoletos tan pronto como se inventó awk a mediados de la década de 1970 y solo las personas que disfrutan resolviendo problemas utilizando la sintaxis arcana de seds en lugar de hacerlo de manera simple y sencilla en awk, se volvieron obsoletos. Si está utilizando más de s, gyp (con -n) en sed, es casi seguro que esté utilizando la herramienta incorrecta.
Ed Morton
26
Morton awk trabaja con datos estructurados (cada línea tiene la misma estructura). Sed está diseñado para trabajar con datos aleatorios sin procesar. Por lo tanto, no puede simplemente usar awk en lugar de sed.
Pithikos
5
Recomiendo encarecidamente leer info sed. Es mucho más detallado que la simple página de manual.
Fernando Basso
4
Estoy de acuerdo con Pithikos. Bajé por el camino como lo hizo Morton y me hice la misma pregunta que Morton. Sin embargo, todavía no podía descartar sed tan fácilmente.
eigenfield

Respuestas:

111

Cuando sed lea un archivo línea por línea, la línea que se ha leído actualmente se inserta en el modelo de memoria intermedia (espacio de patrones). El búfer de patrones es como el búfer temporal, el bloc de notas donde se almacena la información actual. Cuando le dice a sed que imprima, imprime el búfer de patrón.

Mantener búfer / espacio de espera es como un almacenamiento a largo plazo, de modo que puede capturar algo, almacenarlo y reutilizarlo más tarde cuando sed esté procesando otra línea. No procesa directamente el espacio de reserva, en su lugar, debe copiarlo o agregarlo al espacio del patrón si desea hacer algo con él. Por ejemplo, el comando de impresión pimprime solo el espacio del patrón. Asimismo, sopera sobre el espacio del patrón.

Aquí hay un ejemplo:

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

(la opción -n suprime la impresión automática de líneas)

Hay tres comandos aquí: 1!G, hy $p. 1!Gtiene una dirección, 1(primera línea), pero !significa que el comando se ejecutará en todas partes menos en la primera línea. $ppor otro lado, solo se ejecutará en la última línea. Entonces, lo que pasa es esto:

  1. la primera línea se lee e inserta automáticamente en el espacio del patrón
  2. en la primera línea, el primer comando no se ejecuta; hcopia la primera línea en el espacio de espera .
  3. ahora la segunda línea reemplaza lo que estaba en el espacio del patrón
  4. en la segunda línea, primero ejecutamos G, agregando el contenido del búfer de retención al búfer de patrón, separándolo por una nueva línea. El espacio del patrón ahora contiene la segunda línea, una nueva línea y la primera línea.
  5. Luego, el hcomando inserta el contenido concatenado del búfer de patrón en el espacio de espera, que ahora contiene las líneas invertidas dos y uno.
  6. Procedemos a la línea número tres: ve al punto (3) anterior.

Finalmente, después de que se haya leído la última línea y se haya agregado el espacio de espera (que contiene todas las líneas anteriores en orden inverso) al espacio del patrón, el espacio del patrón se imprime con p. Como habrá adivinado, lo anterior hace exactamente lo que hace el taccomando: imprime el archivo al revés.

enero
fuente
3
¿La opción G y h funciona como "cortar y añadir"? No parece una operación de "copiar y agregar".
Sonríe el
¿Qué se agrega con el patrón y el espacio de retención cuando se usan comandos anidados (llaves)? '195,210{/add/p}'… ¿Es posible extraer la última línea de un grupo de líneas involucradas en un patrón?
Sandburg
17

@Ed Morton: No estoy de acuerdo contigo aquí. Encontré sedmuy útil y simple (una vez que asimilas el concepto del patrón y mantienes los búferes) para encontrar una forma elegante de hacer grepping multilínea.

Por ejemplo, tomemos un archivo de texto que tiene nombres de host y algo de información sobre cada host, con mucha basura en el medio que no me importa.

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

Para mí, un script awk para obtener las líneas con el nombre de host y la infolínea correspondiente tomaría un poco más de lo que puedo hacer con sed:

sed -n '/Host:/{h}; /Info/{x;p;x;p;}' myfile.txt

la salida se ve así:

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!!

(Tenga en cuenta que Host: foo1aparece dos veces en la salida).

Explicación:

  1. -n desactiva la salida a menos que se imprima explícitamente
  2. primera coincidencia, busca y coloca la Host:línea en el búfer de retención (h)
  3. segunda coincidencia, encuentra la siguiente línea Info:, pero primero intercambia (x) la línea actual en el búfer de patrón con el búfer de retención, e imprime (p) la Host:línea, luego vuelve a intercambiar (x) e imprime (p) la línea Info :.

Sí, este es un ejemplo simplista, pero sospecho que este es un problema común que se solucionó rápidamente con un simple sed one-liner. Para tareas mucho más complejas, como aquellas en las que no puede confiar en una secuencia predecible y dada, awk puede ser más adecuado.

Jens Jensen
fuente
2
En este caso, aunque podría usar grep:grep 'Host\|Info'
Pithikos
Si hay dos líneas de información después de un host determinado, entonces @JensJenson quiere que ambas líneas de información estén precedidas por una línea de información. Creo que editaré la respuesta en consecuencia. Pithikos, grep no será suficiente entonces.
Aaron McDaid
3
@JensJenson, el awkequivalente de su código sed también es bastante corto:awk '/Host:/{hold=$0}; /Info/{print hold; print;}' myfile.txt
Aaron McDaid
11

Aunque la respuesta de @ January y el ejemplo son agradables, la explicación no fue suficiente para mí. Tuve que buscar y aprender mucho hasta que logré entender cómo sed -n '1!G;h;$p'funciona exactamente . Así que me gustaría dar más detalles sobre el comando para alguien como yo.

En primer lugar, veamos qué hace el 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

Invierte la entrada como lo tachace el comando.

sedlee línea por línea, así que veamos qué sucede en el espacio de patrón y el espacio de espera en cada línea. Como el hcomando copia el contenido del espacio del patrón en el espacio de espera, ambos espacios tienen el mismo texto.

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

En la última línea, se $pimprime con d\nc\nb\na$el formato

d
c
b
a

Si desea ver el espacio del patrón para cada línea, puede agregar 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

Me resultó muy útil ver este video tutorial Comprender cómo funciona sed , ya que el chico muestra cómo se usará cada espacio paso a paso. La retención espaciada se hace referencia en el cuarto tutorial, pero recomiendo ver todos los videos si no está familiarizado sed.

También el documento sed de GNU y el tutorial Sed de Bruce Barnett son muy buenas referencias.

Sanghyun Lee
fuente
2
Creo que también será útil mencionar que el espacio de espera para todos los propósitos prácticos está vacío a menos que agreguemos algo.
Naveed