Sed en una-línea (sed one-liners) Parte III

Sed en una-línea, Parte III

Famous Sed One-Liners Explained, Part III

Dado que no hay muy buena documentación en español acerca del comando sed, me decidí a traducir una serie de excelentes artículos creados por Peteris Krumin. Le he pedido permiso expreso a su autor y no tuvo inconvenientes, así que aquí va.

Nota del Traductor (NdT): No será una traducción total, dado que hay mucha información anecdótica y sólo me limitaré a la explicación del comando sed.

Los one-liners de Eric Pement están divididos en varias secciones:

  1. Espaciado de un archivo (explicado en parte I)
  2. Numeración (explicado en parte I)
  3. Conversión y sustitución de texto (explicado en parte I)
  4. Impresión selectiva de ciertas líneas (explicado en parte II
  5. Eliminación selectiva de ciertas líneas (explicado aquí)
  6. Aplicaciones especiales (explicado aquí)

He hecho una cheat sheet que sumariza toda la herramienta sed. Sugiero que la imprimas antes de seguir y la tengas junto a ti. Te ayudará a memorizar los comandos más rápidamente.

5. Eliminación selectiva de ciertas líneas

68. Imprimir todas las líneas en el archivo excepto la sección entre dos expresiones regulares.

sed '/Iowa/,/Montana/d'

Este one-liner continua donde el anterior termina. El one-liner #67 de la parte II usa el rango ‘/start/,/finish/’ para imprimir líneas entre dos expresiones regulares (inclusive). Este one-liner, por otro lado, elimina las líneas entre dos expresiones regulares e imprime el resto. Solo para recordar: un rango ‘/start/,/finish/’ iguala a todas las líneas comenzando desde el principio que igualan a la expresión ‘/start/’ hasta la primer línea que iguala a ‘/finish/’. En este one-liner en particular, el comando ‘d’ es aplicado a estas líneas.

Por ejemplo, supongamos:

Florida
Iowa
New York
San Jose
Montana
Texas
Fairbanks

Luego de que el programa sed termina, la salida sería:

Florida
Texas
Fairbanks

Vemos esta salida, porque las líneas desde Iowa a Montana coinciden con el rango ‘/Iowa/,/Montana/’.

69. Eliminar líneas duplicadas consecutivas de un archivo (emular ‘uniq’).

sed '$!N; /^\(.*\)\n\1$/!P; D'

Este one-liner actúa como la utilidad ‘uniq’. ¿cómo funciona? Primero, por cada línea que no sea la última, sed agrega la siguiente línea al pattern space a través del comando ‘N’. Este comando es restringido a todas las líneas excepto la última por el patrón ‘$!’. La línea recién agregada es separada de la previa por un caracter ‘\n’. A continuación, el pattern space es comparado contra la expresión regular ‘/^\(.*\)\n\1$/’. Esta expresión captura la línea anterior hasta el caracter ‘\n’ y la guarda en el grupo ‘\1′. Entonces testea si la nueva línea agregada es igual a la anterior. Si no lo es, se ejecuta el comando ‘P’ imprimiendo todo el pattern space hasta el caracter ‘\n’, caso contrario, no se ejecuta. A continuación el comando ‘D’ elimina todo hasta el primer ‘\n’, dejando solo la última línea leída en el pattern space. Además fuerza a sed a ejecutar el script desde el inicio.

Veamos un ejemplo:

foo
foo
foo
bar
bar
baz

La primera cosa que hace sed es leer la primera línea y ponerla en el pattern space. Ahora contiene ‘foo’. Luego el comando ‘N’ se ejecuta y el pattern space ahora contiene ‘foo\nfoo’. A continuación el pattern space es testeado nuevamente contra la expresión regular ‘/^\(.*\)\n\1$/’. Esta expresión coincide porque ‘\(.*\)’ es ‘foo’ y ‘/^\(.*\)\n\1$/’ es ‘foo\nfoo’, tal cual lo que tenemos en el pattern space. Como coincide, el comando ‘P’ no es ejecutado y el comando ‘D’ entra en ejecución eliminando todo hasta el primer ‘\n’ del pattern space que hora solo contiene ‘foo’. Sed comienza a ejecutar el script nuevamente. Se ejecuta ‘N’, el pattern space contiene ahora ‘foo\nfoo’ nuevamente y las mismas cosas suceden: ‘P’ no se ejecuta y ‘D’ elimina todo hasta ‘\n’ dejando en el pattern space solo ‘foo’. Nuevamente ‘N’ es ejecutado y esta vez ‘bar’ se agrega al pattern space que ahora contiene ‘foo\nbar’. La expresión regular ‘/^\(.*\)\n\1$/’ no coincide y ‘P’ se ejecuta imprimiendo ‘foo’, luego ‘D’ lo borra dejando solo ‘bar’. Se reinicia el script y ‘N’ se ejecuta, agrega ‘bar’ con lo cual queda ‘bar\nbar’. Al igual que con ‘foo\nfoo’, no se imprime nada y se repiten los pasos mencionados. Por último ‘N’ agrega ‘baz’ quedando ‘bar\nbaz’ lo que hace que no coincida la expresión regular. ‘P’ se ejecuta e imprime y ‘D’ elimina ‘bar’ dejando sólo ‘baz’ en el pattern space. Ahora ‘N’ no se ejeuta porque es la última línea de entrada. La restricción ‘$!N’ en este momento hace que las expresiones regulares no coincidan y se imprime el pattern space que solo contenía ‘baz’. ‘D’ limpia el pattern space y al no haber mas entrada, sed termina.

La salida sería:

foo
bar
baz

70. Eliminar líneas duplicadas no consecutivas de un archivo.

sed -n 'G; s/\n/&&/; /^\([ -~]*\n\).*\n\1/d; s/\n//; h; P'

Este es algo complicado. Almacena las líneas únicas en el hold buffer y por cada nueva línea leída verifica que no exista en el hold buffer. Si existe, la línea leída es descartada. Si no, es agregada al hold buffer.

71. Elimina todas las líneas excepto las duplicadas consecutivas (emula ‘uniq -d’).

sed '$!N; s/^\(.*\)\n\1$/\1/; t; D'

Este one-liner imprime solo las líneas duplicadas. Comienza leyendo la primera línea con el comando ‘N’. Como se mencionó, la línea actual y la siguiente son separadas con un ‘\n’. Además el script restringe el comando ‘N’ a todas las líneas excepto la última. Ahora se intenta una sustitución ‘s/^\(.*\)\n\1$/\1/’. Al igual que en #69, esta sustitución reemplaza dos cadenas repetidas. Ahora si la sustitución tuvo éxito el comando ‘t’ lleva el script a su fin, imprimiendo el pattern space. Si la sustitución no sucedió se ejecuta ‘D’ borrando la cadena no-repetida. El ciclo continúa y sólo las líneas duplicadas son mostradas una vez.

Un ejemplo:

foo
foo
bar
baz

El resultado sería:

foo

Tal como esperábamos, sólo la línea duplicada es mostrada.

72. Elimina las 10 primeras líneas de un archivo.

sed '1,10d'

Este one-liner restringe al comando ‘d’ a un rango de líneas. El ‘1,10’ indica el rango de líneas de 1 a 10 inclusive. En cada una de estas líneas el comando ‘d’ es ejecutado borrando el pattern space y recomenzando. La acción es que imprimirá todas las líneas por encima de la décima.

73. Delete the last line of a file.

sed '$d'

Este one-liner restringe al comando ‘d’ a la última línea. Como resultado, la última línea es eliminada.

74. Eliminar las últimas dos líneas de un archivo.

sed 'N;$!P;$!D;$d'

Este one-liner siempre mantiene dos líneas en el pattern space. En la última línea simplemente no muestra estas últimas dos. Tan pronto como sed lee la primer línea en el pattern space se ejecuta el primer comando ‘N’. Este coloca la segunda línea en el pattern space. Los dos siguiente comandos ‘$!P’ y ‘$!D’ imprimen la primera parte del pattern space hasta ‘\n’ y borra esta parte. Se continúa haciendo lo mismo hasta que la última línea es agregada al pattern space. En este momento ‘$d’ se ejecuta eliminandolas.

Si hubiese una sola línea, entonces solo se imprime.

75. Eliminar las últimas 10 líneas de un archivo.

sed -e :a -e '$d;N;2,10ba' -e 'P;D'

Siempre mantiene 10 líneas en el pattern space, agregándolas cada nueva y borrando la 11va con ‘D’. Una vez que se alcanza el fin de archivo, ‘d’ borra el pattern space eliminando las últimas 10 líneas

Esta es otra forma

sed -n -e :a -e '1,10!{P;N;D;};N;ba'

76. Eliminar cada 8 líneas.

gsed '0~8d'

Este funciona sólo con GNU Sed. Usa una dirección especial que marca una línea como la primera y salta n pasos. El 0 no es válido. En este caso la primera línea a coincidir será 8, luego 16, luego 24, etc. Cada línea será eliminada por el comando ‘d’.

Otra forma usando sed sería:

sed 'n;n;n;n;n;n;n;d;'

77. Eliminar las líneas que coincidan con un patrón.

sed '/pattern/d'

Este one-liner ejecuta el comando ‘d’ borrando todas las líneas que coincidan con ‘/pattern/’.

78. Borrar todas las líneas en blanco de un archivo (emula “grep ‘.'”).

sed '/^$/d'

La expresión regular ‘/^$/’ pregunta si es el comienzo de la línea coincide con el final de la misma. Solo las líneas vacías tienen esta propiedad, asi que sed las elimina.

Otra forma sería:

sed '/./!d'

En este caso si la línea coincide al menos con un caracter (representado por ‘.’) es omitida. Si no coincide es porque es una línea vacía y es borrada.

79. Eliminar todas las líneas en blanco consecutivas de un archivo (emula “cat -s”).

sed '/./,/^$/!d'

En este caso deja una línea en blanco al final del archivo si hubiera muchas. Pero si eso no ocurre, todas las líneas en blanco son eliminadas. Usa ‘/start/,/finish/!’ para borrar con ‘d’ desde la primer línea en blanco hasta la primera no-vacía, no inclusive.

Otra forma:

sed '/^$/N;/\n$/D'

Si hubiera muchas lineas vacías al principio y al final, este deja solo una al principio y una al final. En otro caso, elimina todas las consecutivas.

80. Eliminar todas las líneas vacías consecutivas de un archivo excepto las primeras dos.

sed '/^$/N;/\n$/N;//D'

En caso de más de 2 líneas vacías, este one-liner las elimina dejando solo dos.

Veamos el último comando ‘//D’. Es un atajo a ‘/lo-que-coincidió-anteriormente/D’. En este caso ‘/\n$/D’.En cada línea vacía, se agrega al pattern space con el comando ‘/^$/N’. A continuación se verifica si la línea recién leída es en realidad una línea vacía con el comando ‘/\n$/’. Si lo es, entonces se lee otra línea con ‘N’. En este momento se repite el mismo test: ‘/\n$/’. Si la línea es vacía, elimina la primera línea en blanco y reinicia el script desde el principio. Note que hay 2 líneas vacías consecutivas todo el tiempo en el pattern space. Así, todas las líneas vacías se eliminan dejando solo dos (contenidas en el pattern space e impresas al terminar sed).

81. Eliminar todas las líneas vacías al inicio de un archivo.

sed '/./,$!d'

No hay mucha explicación que dar, hace lo que indica.

82. Elimina todas las líneas vacías al final de un archivo.

sed -e :a -e '/^\n*$/{$d;N;ba' -e '}'

Este one-liner acumula las líneas vacías en el pattern space hasta que encuentra una línea no-vacía o el final de archivo. Si termina, se ejecuta ‘d’ borrando todo el pattern space y termina. Si encuentra una línea no-vacía, el pattern space es impreso y el script continúa. (un caso sería la separación de parrafos con más de una línea en blanco).

Para gsed:

gsed -e :a -e '/^\n*$/N;/\n$/ba'

83. Eliminar la última línea de cada párrafo.

sed -n '/^$/{p;h;};/./{x;/./p;}'

Este one-liner siempre mantiene la línea anterior en el hold buffer.Lo consigue a través del segundo bloque de comandos ‘/./{x;/./p;}’. En este bloque, el pattern space (1 línea) es intercambiada con el hold buffer a través del comando ‘x’ y si el hold buffer no estaba vacío se imprime por ‘p’. El siguiente momento a notar es saber que sucede en la primer línea vacía. Esa es la línea siguiente al párrafo. En este momento ‘/^$/{p;h;}’ es ejecutado, se imprime la línea en blanco (pero no imprime la última línea del párrafo!), y coloca la línea vacía en el hold buffer. Una vez que un nuevo párrafo es alcanzado, el script se ejecuta nuevamente como si se tratara del primer párrafo.

6. Aplicaciones Especiales

84. Eliminar nroff overstrikes.

Nroff overstrikes son caracteres que están formateados para permanecer en negrita. En las antiguas máquinas de escribir, uno tipeaba la letra, pulsaba backspace para volver un caracter y volvía a tipear la letra, remarcándola. En nroff sería CHAR, CTRL+H, CHAR. Este one-liner elimina los CHAR, CTRL+H, dejando simplemente el último CHAR.

sed 's/.^H//g'

Para insertar el caracter de control Ctrl+H se pulsa Ctrl+V y luego Ctrl+H (asi se inserta ^H). Luego se usa el comando de sustitución para borrar cualquier caracter ‘.’ seguido de Ctrl+H ^H.

Otra manera de hacer lo mismo es usar una expresión de escape en hexadecimal:

sed 's/.\x08//g'

Incluso existe una manera más, usando el comando echo (note que las comillas invertidas ` ` indican la ejecución de un comando externo a sed y el resultado que arroje será reemplazado por lo que hay entre las comillas):

sed 's/.'`echo -e "\b"`'//g'

85. Imprimir cabecera de mensaje Usenet/HTTP/Email.

gsed -r '/^\r?$/q'

Las cabeceras de Usenet, HTTP y Email son muy similares. Son un conjunto de líneas de texto, separadas del cuerpo del mensaje con dos nuevas líneas ‘\r\n\r\n’. Este one-liner termina en la primer línea que es vacía o contiene ‘\r’. En otras palabras, imprime la cabecera y termina.

86. Imprimri el cuerpo de mensaje Usenet/HTTP/Email.

sed '1,/^$/d'

Este one-liner usa un rango ‘1,/^$/’ para eliminar las líneas desde 1 hasta la última línea en blanco (inclusive). Como se explicó en el anterior #78, ‘ /^$/’ coincide con líneas vacías. Todas las líneas anteriores a la primer línea en blanco son la cabecera y serán borradas.

87. Extraer el Asunto de un mensaje de e-mail.

sed '/^Subject: */!d; s///; q'

Este one-liner elimina todas las líneas que no coincidan con ‘^Subject: ‘. Luego lo reutiliza ‘s///’ para eliminar ‘Subject: ‘ de la línea dejando solamente el asunto real.

88. Extraer información de quién envía de un mensaje de email.

sed '/^From: */!d; s///; q'

Este one liner es equivalente al anterior, excepto que imprime la información de quien envía.

89. Extraer una dirección de email de la cadena “Nombre Apellido <email@domain.com>”.

sed 's/.*< *//;s/ *>.*//;

Este one-liner elimina cualquier símbolo antes de < y luego de >.

90. Agrega un > al principio de cada línea (responder a un email).

sed 's/^/> /'

Este one-liner coloca un caracter ‘>’ al principio de cada línea.

91. Elimina el > inicial de cada línea.

sed 's/^> //'

Hace justamente lo que indica, además elimina el espacio siguiente al caracter >.

92. Quitar las etiquetas HTML.

sed -e :a -e 's/<[^>]*>//g;/</N;//ba'

Sed no fue hecho para analizar HTML. Comienza creando una etiqueta ‘a’. Luego en cada línea sustituye ‘<[^>]*>’ con ningún caracter tantas veces como sea posible (‘g’). La expresión ‘<[^>]*>’ indica ‘<‘ seguido de cualqueir otro símbolo que no son ‘>’, y que termina con ‘>’.

Disfruta!

Este post completa la tercer parte del artículo Sed en una línea. Espero que hayas aprendido alguna cosa o dos. Fue un placer haberlas explicado.

Fin de la traducción.

Créditos a Peteris Krumin!!

About these ads

1 comentario (+¿añadir los tuyos?)

  1. Trackback: Famous Sed One-Liners Explained, Part III - good coders code, great reuse

Deja un comentario

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

Archivos

julio 2009
D L M X J V S
« jun   sep »
 1234
567891011
12131415161718
19202122232425
262728293031  
Seguir

Recibe cada nueva publicación en tu buzón de correo electrónico.

Únete a otros 95 seguidores

%d personas les gusta esto: