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
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 sólo
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
sólo 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, sólo
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
|