Gentoo Logo

Renuncia de responsabilidad: La versión original de este artículo fue publicada por IBM developerWorks y es propiedad de Westtech Information Services. Este documento es una versión actualizada del artículo original y contiene mejoras introducidas por el Equipo de Documentación de Gentoo.
Este documento carece de soporte activo.


Sed mediante ejemplos, Parte 1

Contenido:

1.  Dar a conocer el poderoso editor UNIX

Elegir un editor

En el mundo UNIX, disponemos de muchas opciones cuando necesitamos editar archivos. Pensando en ello -- vienen a la mente vi, emacs y jed, así como muchos otros. Todos nosotros tenemos nuestro editor favorito (así como nuestras combinaciones de teclas favoritas) que hemos llegado a conocer y ahora amamos. Con nuestro editor de confianza, estamos listos para manejar cualquier tarea de administración relacionada con UNIX o de programación con facilidad.

Mientras que los editores interactivos son buenos, tienen algunas limitaciones. A pesar de que su naturaleza interactiva puede ser un punto fuerte, también puede ser una debilidad. Consideremos una situación en la que necesitemos hacer un tipo muy similar de cambios en un grupo de archivos. Podemos lanzar nuestro editor de textos favorito y realizar una labor mundana, tediosa y repetitiva, malgastando nuestro tiempo en muchas ediciones a mano. Pero hay una forma mejor de hacerlo

Introducción a sed

Sería bueno poder automatizar el proceso de editar archivos, de forma que podamos "procesar por lotes" la edición de archivos, e incluso crear macros con la habilidad de realizar cambios sofisticados a archivos existentes. Afortunadamente para nosotros y para este tipo de situaciones, hay un método mucho mejor -- y este método se denomina sed.

sed es un ligero editor de flujo que está incluido en casi todos los sabores UNIX, Linux incluido. sed tiene muchas buenas características. La primera de ellas es que es de peso muy ligero, normalmente muy inferior al de nuestro lenguaje favorito de macros. En segundo lugar, dado que sed es un editor de flujo, puede editar los datos que recibe de stdin, como aquellos redireccionados. Por lo que no se necesita tener los datos a editar almacenados en un archivo del disco. Dado que los datos pueden redirigirse a sed, es muy fácil usar sed como parte de un largo y complejo redireccionamiento en un archivo por lotes de nuestro intérprete de comandos. Intentemos hacerlo con nuestro editor favorito.

sed GNU

Afortunadamente para nosotros, usuarios de Linux, una de las mejores versiones disponibles de sed es la versión de GNU, actualmente en su versión 3.02. Cada distribución de Linux posee el editor sed de GNU o, al menos, debería. El sed de GNU es muy conocido, no únicamente porque su código fuente sea de libre distribución, sino porque tiene muchas extensiones del estándar POSIX de sed que nos evitarán desperdiciar el tiempo. El sed de GNU tampoco sufre muchas de las limitaciones del sed anterior y propietario, como una longitud de línea limitada -- El sed de GNU puede manejar líneas de cualquier tamaño con facilidad.

El último sed de GNU

Mientras elaboraba este artículo, noté que varios aficionados de sed hacían referencia al sed de GNU 3.02a. A pesar de que no pude encontrarlo en ftp://ftp.gnu.org (ver los Recursos para encontrar estos enlaces), por lo que tuve que buscarlo en otra parte. Lo encontré en ftp://alpha.gnu.org, bajo /pub/sed. Lo descargué, compilé e instalé para observar, pasados unos minutos, que la última versión de sed es la 3.02.80 -- y puede encontrarse su código fuente justo al lado de las de 3.02a, en ftp://alpha.gnu.org. Después de tener el sed de GNU 3.02.80 instalado, ya estaba listo para continuar.

El sed correcto

En esta serie, utilizaré el sed de GNU versión 3.02.80. Algunos (aunque pocos) de los ejemplos más avanzados en mi serie de artículos acerca de sed no funcionarán con el sed de GNU 3.02 ó 3.02a. Si se está usando un sed que no sea el de GNU los resultados pueden variar. ¿Por qué no nos tomamos el tiempo necesario para instalar el sed 3.02.80 de GNU ahora? Después, no solo estaremos preparados para el resto de los artículos acerca de sed, sino que estaremos usando, indiscutiblemente, el mejor sed que existe.

Ejemplos con sed

Sed trabaja realizando cualquier número de operaciones de edición especificadas por el usuario ("comandos") en los datos de entrada. Sed se basa en líneas, por lo que los comandos se realizan en cada línea, siguiendo un orden. Sed, escribe sus resultados en la salida estándar (stdout); por lo que no modifica ninguno de los archivos de entrada.

Veamos algunos ejemplos. Los primeros van a ser un poco inútiles, debido a que pretendo mostrar con ellos cómo trabaja sed, en lugar de realizar cualquier tarea útil. De cualquier modo, si somos principiantes con sed, es muy importante entenderlos. He aquí nuestro primer ejemplo:

Listado de Código 1.1: Ejemplo de uso de sed

$ sed -e 'd' /etc/services

Si tecleamos este comando, no obtendremos absolutamente ningún mensaje. ¿Qué ha ocurrido? En este ejemplo, hemos llamado a sed con un comando de edición, d. Sed abrió el archivo /etc/services, leyó una línea en su memoria intermedia, realizó nuestro comando de edición ("borrar una línea") y después mostró su memoria intermedia de patrones (la cual estaba vacía). Después repitió estos pasos para cada línea sucesivamente. Con lo cual no se produjo ningún mensaje, dado que el comando d erradicó todas y cada una de las líneas en la memoria intermedia de patrones.

Hay un par de cosas a considerar en este ejemplo. La primera, /etc/services no fue modificado en absoluto. Esto se debe a que sed únicamente lee del archivo que se le indica en la línea de comandos, usándolo como entrada -- ni siquiera intenta modificarlo. La segunda es que sed está orientado hacia líneas. El comando d no le dijo a sed que borrara todos los datos sin la más mínima precaución. En su lugar, sed leyó línea por línea /etc/services en su memoria intermedia interna, denominada memoria intermedia de patrones (pattern buffer). Cada vez que se leía una línea en la memoria intermedia de patrones, realizaba el comando d e imprimía los contenidos de la memoria intermedia de patrones (ninguno en este ejemplo). Después mostraré cómo usar rangos de direcciones para controlar las líneas a las que se aplica un comando -- aunque en ausencia de los mismos, un comando se aplica a todas las líneas.

La tercera cosa a notar es el uso de comillas simples para introducir el comando d. Es una buena idea adquirir el hábito de usar comillas simples para introducir los comandos sed, para deshabilitar la expansión del intérprete de comandos.

Otro ejemplo de uso de sed

He aquí un ejemplo de cómo usar sed para eliminar la primera línea del archivo /etc/services de nuestro flujo de salida:

Listado de Código 1.2: Otro ejemplo de uso de sed

$ sed -e '1d' /etc/services | more

Como puede verse, este comando es muy similar a nuestro primer comando d exceptuando que se encuentra precedido por un 1. Si al verlo hemos pensado que el 1 hacía alusión a la línea 1, estábamos en lo cierto. Mientras que en nuestro primer ejemplo usamos d por sí mismo, en este caso usamos el comando d precedido por una dirección lineal opcional. Usando direcciones, podemos decirle a sed que edite únicamente una línea o solo unas líneas.

Rangos de direcciones

Ahora, veamos cómo especificar rangos de direcciones. En este ejemplo, sed borrará de la línea 1 a la 10 de la salida:

Listado de Código 1.3: Especificar un rango de direcciones

$ sed -e '1,10d' /etc/services | more

Cuando separamos dos direcciones por una coma, sed aplicará el siguiente comando al rango que empieza con la primera dirección y terminará con la segunda dirección. En este ejemplo, el comando d se ha aplicado desde la línea 1 hasta la línea 10, inclusive. El resto de líneas son ignoradas.

Direcciones con expresiones regulares

Ha llegado el momento de un ejemplo mucho más útil. Digamos que queríamos ver los contenidos del archivo /etc/services, pero no estamos interesados en ver ninguno de los comentarios incluidos. Como todos sabemos, se pueden añadir comentarios en nuestro archivo /etc/services comenzando la línea con un carácter '#'. Para evitar los comentarios, le indicamos a sed que elimine todas las líneas que comienzan con '#'. He aquí cómo hacerlo:

Listado de Código 1.4: Eliminar las líneas que comienzan con #

$ sed -e '/^#/d' /etc/services | more

Intentemos realizar este ejemplo y veamos lo que ocurre. Notaremos que sed realiza la labor solicitada con gran rapidez. Vamos a figurarnos qué es lo que ha ocurrido.

Para comprender el comando '/^#/d', antes necesitamos diseccionarlo. Primero, eliminemos la 'd' -- estamos usando el mismo comando de borrado de líneas que hemos usado previamente. La nueva parte añadida es la '/^#/', que es un nuevo tipo de dirección mediante una expresión regular. Las direcciones con expresiones regulares se indican siempre entre barras. Especifican un patrón y el comando que está a continuación de una dirección con expresión regular únicamente se aplicará a una línea si encuentra dicho patrón en ella.

Por lo que '/^#/' es una expresión regular. Pero, ¿qué es lo que hace? Obviamente, este sería un buen momento para repasar las expresiones regulares.

Repaso a las expresiones regulares

Podemos usar expresiones regulares para expresar patrones que podemos encontrar en el texto. Si hemos usado alguna vez el carácter '*' en un comando del intérprete de comandos, hemos usado algo muy similar, aunque no idéntico, a las expresiones regulares. Aquí tenemos los caracteres especiales que pueden usarse en las expresiones regulares:

Carácter Descripción
^ Apunta al comienzo de la línea
$ Apunta al final de la línea
. Apunta a un único carácter
* Apunta a cero o más ocurrencias del carácter previo
[ ] Apunta a todos los caracteres entre los corchetes

Veamos algunos ejemplos con expresiones regulares para facilitar las cosas. Todos estos ejemplos serán aceptados por sed como direcciones válidas que pueden aparecer a la izquierda de cualquier comando:

Expresión regular Descripción
/./ Apuntará a cualquier línea que contenga al menos un carácter
/../ Apuntará a cualquier línea que contenga al menos dos caracteres
/^#/ Apuntará a cualquier línea que comience con un '#'
/^$/ Apuntará a cualquier línea en blanco
/}$/ Apuntará a toda línea que termine con un '}' (sin espacios)
/} *$/ Apuntará a toda línea que termine con un '}' con cero o más espacios
/[abc]/ Apuntará a toda línea que contenga una 'a', 'b', o 'c' minúscula
/^[abc]/ Apuntará a cualquier línea que empiece con 'a', 'b', o 'c'

Recomiendo encarecidamente intentarlo con varios de estos ejemplos. Tomarse el tiempo necesario para familiarizarse con las expresiones regulares, e intentar usar algunas expresiones regulares de nuestra propia invención. Puede usarse una expreg de este modo:

Listado de Código 1.5: Forma adecuada de usar expreg

$ sed -e '/expreg/d' /ruta/a/mi/archivo/de/pruebas | more

Esto dará lugar a que sed borre cualquier línea coincidente. De todos modos, puede ser más sencillo familiarizarse con las expresiones regulares pidiéndole a sed que muestre las coincidencias con expreg y que borre las que no coincidan, en lugar de seguir el camino opuesto. Lo cual puede lograrse con el siguiente comando:

Listado de Código 1.6: Imprimir las coincidencias expreg

$ sed -n -e '/expreg/p' /ruta/a/mi/archivo/de/pruebas | more

Hay que tomar nota de la opción '-n', que indica a sed que no imprima el espacio en el patrón a menos que se le indique. También observamos que hemos sustituido el comando d con el comando p, que como se podrá pensar solicita a sed que muestre el espacio entre patrones. Por lo tanto, solo se mostrarán las expresiones que coincidan.

Más direcciones

Hasta ahora, hemos echado un vistazo a direcciones lineales, direcciones en rangos lineales y direcciones expreg. Pero aún hay más posibilidades. Podemos especificar dos expresiones regulares separadas por una coma, y sed marcará todas las líneas que comiencen con la primera expresión regular seleccionada hasta la línea (incluida) que contenga la segunda expresión regular. Por ejemplo, el siguiente comando mostrará un bloque de texto que comience con una línea que contenga "PRINCIPIO", y termine con una línea que contenga "FIN":

Listado de Código 1.7: Imprimir el bloque deseado de texto

$ sed -n -e '/PRINCIPIO/,/FIN/p' /mi/archivo/de/pruebas | more

Si "PRINCIPIO" no se encuentra, no se imprimirá ningún dato. Y si se encuentra "PRINCIPIO" pero no se encuentra ninguna línea que contenga "FIN" a continuación, todas las líneas siguientes se imprimirán. Esto ocurre debido a la naturaleza basada en flujo de sed -- desconoce si "FIN" aparecerá o no.

Ejemplo con código fuente en C

Si únicamente se quiere imprimir la función main() en un archivo de código fuente en C, se podría teclear:

Listado de Código 1.8: Imprimir la función main() en un archivo de código fuente en C

$ sed -n -e '/main[[:space:]]*(/,/^}/p' sourcefile.c | more

Este comando tiene dos expresiones regulares, '/main[[:space:]]*(/' y '/^}/', y un comando , p. La primera expresión regular apuntará a la cadena "main" seguida de cualquier número de espacios o tabuladores, seguida además por un paréntesis abierto. Lo cual debería coincidir con el comienzo de nuestra declaración main() en ANSI C.

En este caso de expresión regular, encontramos la clase de caracteres '[[:space:]]'. La cual es únicamente una palabra clave especial para sed que le indica a sed que apunte a cualquier espacio o TAB. De haberlo querido, en lugar de teclear '[[:space:]]', podríamos haber tecleado '[', después un espacio literalmente, a continuación Control-V, después un TAB literal y un ']' -- Control-V indica a bash que queremos introducir un TAB "real" en lugar de una expansión del comando. Es mucho más claro, especialmente en archivos de comandos usar la clase de comandos '[[:space:]]'.

Bien, ahora la segunda expreg. '/^}/' apuntará a algún carácter '}' que aparezca al comienzo de una nueva línea. Si nuestro código se ha formateado correctamente, toparemos con ello con el final de nuestra función main(). Si no lo está, no lo hará -- cuestión de trabajar con la coincidencia de patrones.

El comando p hace lo mismo que siempre, le dice a sed que imprima explícitamente la línea, dado que estamos en el modo silencioso '-n'. Si se intenta ejecutar el comando en una línea de código fuente en C, tratará de mostrar el bloque completo main() { }, incluyendo el "main ()" inicial y el '}' final.

La próxima vez

Ahora que hemos tratado los principios básicos, estamos listos para los siguientes dos artículos. Si necesitamos más material acerca de sed, hay que ser paciente -- se está elaborando. Mientras tanto, pueden consultarse los siguientes recursos acerca de sed y de las expresiones regulares.

2.  Recursos

Enlaces útiles



Imprimir

Página actualizada 2 de enero, 2012

Sumario: En esta serie de artículos, Daniel Robbins mostrará cómo usar el poderoso (aunque muy a menudo olvidado) editor de flujo UNIX, sed. Sed es una herramienta ideal para editar archivos por lotes o para crear macros en el intérprete de comandos que modifiquen archivos existentes de forma muy poderosa.

Daniel Robbins
Autor

LinuxBlues
Traductor

Donate to support our development efforts.

Copyright 2001-2014 Gentoo Foundation, Inc. Questions, Comments? Contact us.