¿Cómo el comando sed '1! G; h; $! D' revierte el contenido de un archivo?

20

Mi pregunta está relacionada con la sedsolución específica dada en esta respuesta para esta pregunta de grepping inverso . La solución sed/ grepque no puedo descifrar es la siguiente:

sed '1!G;h;$!d' file

¿Alguien puede descifrar este comando?

Sé por el conocimiento de VI (M) que G denota la última línea del archivo y que en sed bang (!) Seguido de una dirección funciona un poco así, grep -ves decir que no coincidirá con esa línea. Pero en general, el script sed en línea anterior está más allá de mí.

Friki
fuente
3
... muy lentamente ...
mikeserv 01 de
1
Como insinúa mikeserv. Hay que tener en cuenta que esta sedreceta difícil es una forma extremadamente ineficiente (O (n ^ 2/2) complejidad) para invertir las líneas en un archivo. Sería prohibitivamente lento para archivos con una gran cantidad de líneas. Para una alternativa de inversión de orden de línea mucho más eficiente, ver tacde GNU coreutils.
arielf

Respuestas:

35

Esto invierte el archivo línea por línea.

Archivo sed '1! G; h; $! d'

Primero, sedtiene un espacio de espera y un espacio de patrón . Tenemos que distinguir entre ellos antes de concentrarnos en ese comando específico.

Cuando sedlee una nueva línea, se carga en el espacio del patrón. Por lo tanto, ese espacio se sobrescribe cada vez que se procesa una nueva línea. Por otro lado, el espacio de retención es consistente en todo el procesamiento y los valores pueden almacenarse allí para su uso posterior.


Al comando:

Hay 3 comandos de esta declaración: 1!G, hy$!d

  • 1!Gsignifica que el Gcomando se ejecuta en cada línea excepto en la primera ( !niega la 1). Gsignifica agregar lo que está en el espacio de espera al espacio del patrón.

  • hse aplica a cada línea. It copias espacio de patrones para el espacio de la bodega (y sobrescribe).

  • $!dse aplica a todas las líneas excepto la última ( $representa la última línea, la !niega). des el comando para eliminar la línea (espacio del patrón).


  1. Ahora, cuando se lee la primera línea, sedejecuta el hcomando. La primera línea se copia en el espacio de espera. Luego se elimina, ya que coincide con la $!condición. sedcontinúa con la segunda línea.
  2. La segunda línea coincide con la condición 1!(no es la primera línea), por lo que el espacio de espera (que tiene la primera línea) se agrega al espacio del patrón (que tiene la segunda línea). Después de eso, en el espacio del patrón, ahora está la segunda línea seguida de la primera línea, delimitada por una nueva línea. Ahora, se haplica el comando (como en cada línea); todo lo que está en el espacio del patrón se copia en el espacio de espera. Se $!daplica la tercera declaración ( ): la línea se elimina del espacio del patrón.
  3. El paso 2 ahora se realiza con todas las líneas. Saltamos a la última línea.
  4. En la última línea ( $), casi todo el Paso 2 está hecho, pero no la parte de eliminación ( d). sed, cuando se invoca sin -n, imprime el espacio del patrón automáticamente al final del procesamiento para cada línea de entrada. Entonces, cuando no se elimina, se imprime el espacio del patrón. Contiene ahora todas las líneas en orden inverso .
caos
fuente
1
@ Geek No, el hcomando copia el espacio del patrón en el espacio de espera, que persiste hasta el sedfinal. Después del final del script, todo se borra, porque el binario salió.
caos
2
¿Podemos pensar en el espacio de espera como registros para vim? ¿También están numerados? ¿O solo hay uno de ellos?
Geek
2
@ Geek In sedsolo hay un espacio de espera. Es como una variable que puede contener algo.
caos
1
@ user1717828 Si no lo hiciéramos, la primera línea se imprimiría cuando se procesara. Dado que sed no se invoca con -ntenemos que eliminar todas las líneas excepto la última. En la última línea, sed agrega todo, desde el espacio de espera al espacio del patrón. Y debido a que el dcomando no se ejecutará, la línea se imprime (esta línea contiene ahora todo el archivo invertido).
caos
1
(1) La subpregunta / observación del OP (en el comentario) es, "por lo que el hcomando en la última línea es una especie de no-op" (con énfasis adicional y ligeramente parafraseado). El tiene razón; cuando sedprocesa la última línea de entrada , Glee el espacio de retención (para agregarlo al espacio de patrón) y luego hcopia el espacio de patrón al espacio de retención, al que nunca se hace referencia nuevamente . También podríamos decir sed 'G;$!h;$!d'o sed 'G;$!{h;d}'. (2) Podríamos evitar usar ddiciendo sed -n 'G;h;$p'.
Scott