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 2

Contenido:

1.  Cómo aprovechar más el editor UNIX

¡Sustitución!

Veamos uno de los comandos más útiles de sed, el de sustitución. Usándolo, podemos reemplazar una cadena en concreto o la expresión regular coincidente con otra cadena. He aquí un ejemplo del uso más básico de este comando:

Listado de Código 1.1: El uso más básico del comando de sustitución

$ sed -e 's/foo/bar/' miarchivo.txt

El comando anterior sacará los contenidos de miarchivo.txt a stdout, con la primera aparición de 'foo' (si es que hay alguna) reemplazada en cada línea por la cadena 'bar'. Por favor, hay que tener en cuenta que he dicho la primera aparición en cada línea, a pesar de que esto no es lo que se desea normalmente. Cuando quiero realizar la sustitución de una cadena, normalmente la quiero realizar globalmente. Dicho con otras palabras, quiero reemplazar la expresión todas las veces que se muestra, como sigue:

Listado de Código 1.2: Reemplazar todas las coincidencias en cada línea

$ sed -e 's/foo/bar/g' miarchivo.txt

La opción adicional 'g' después de la última barra le indica a sed que debe realizar el reemplazo de forma global.

He aquí algunas otras cosas que se deben saber acerca del comando de sustitución s///. La primera es que se trata de un comando y de un comando únicamente; no hay ninguna dirección mostrada en los ejemplos anteriores. Lo cual significa que el comando s/// puede usarse con directivas acerca de las direcciones a las cuales se aplicará, como se muestra:

Listado de Código 1.3: Especificar las líneas a las que se aplicará el comando

$ sed -e '1,10s/enchantment/entrapment/g' miarchivo2.txt

El ejemplo anterior causará que todas las veces que aparezca 'enchantment' se reemplace por 'entrapment', pero solo hasta la línea 10, inclusive.

Listado de Código 1.4: Especificar más opciones

$ sed -e '/^$/,/^FIN/s/colinas/montañas/g' miarchivo3.txt

Este ejemplo cambiará 'colinas' por 'montañas', pero únicamente en los bloques de texto que comiencen con una línea en blanco, y que terminen con una línea que comience con los tres caracteres 'FIN', inclusive.

Otra de las buenas cosas acerca del comando s/// es que tenemos muchas opciones cuando usamos estos / separadores. Si estamos realizando la sustitución de una cadena y la expresión regular o el reemplazo tiene muchas barras, podemos cambiar el separador especificando un carácter distinto después de la 's'. Por ejemplo, lo siguiente cambiará el contenido cambiando /usr/local por /usr:

Listado de Código 1.5: Reemplazar todas las apariciones de una cadena por otra

$ sed -e 's:/usr/local:/usr:g' milista.txt

Nota: En este ejemplo, estamos usando los dos puntos como separador. Si alguna vez necesitamos especificar el carácter separador en nuestra expresión regular hay que ponerle antes una barra invertida.

La caótica confusión con las expreg

Hasta ahora, únicamente hemos realizado una simple sustitución de cadenas. Aunque esto puede ser muy útil, podemos también buscar expresiones regulares. Por ejemplo, el siguiente comando sed encontrará una frase que comience con '<' y termine con '>', y que contenga cualquier número de caracteres entremedias. Esta frase se borrará (será reemplazada por una cadena sin contenido):

Listado de Código 1.6: Borrar la frase especificada

$ sed -e 's/<.*>//g' miarchivo.html

Un buen comienzo con un archivo de comandos sed sería eliminar todas las etiquetas HTML de un archivo, pero no funcionará de forma correcta, debido a algo que hace especiales a las expresiones regulares. ¿Cuál es la razón? Cuando sed intenta encontrar la expresión regular en una línea, encuentra la coincidencia más larga en la línea. Esto no era ningún problema en mi anterior artículo acerca de sed, dado que estaba usando los comandos d y p, que borrarían la línea completa de todas formas. Pero cuando usamos el comando s///, es muy diferente, dado que todo aquello con lo que coincida la expresión regular será reemplazado con la cadena de sustitución, o en este caso, eliminado. Lo cual significa que, en el ejemplo anterior, convertirá la siguiente línea:

Listado de Código 1.7: ejemplo de código HTML

<b>Esto</b> es lo que <b>I</b> quería decir.

en esta otra:

Listado de Código 1.8: Efecto no deseado

quería decir.

en lugar de hacer esto otro, que era lo que pretendíamos:

Listado de Código 1.9: Efecto deseado

Esto es lo que quería decir.

Afortunadamente, hay una forma sencilla de resolverlo. En lugar de teclear una expresión regular que indique "un carácter '<' seguido de cualquier número de caracteres y que termine con un carácter '>'". Lo cual tendrá el efecto de apuntar a la menor coincidencia posible, en lugar de a la mayor de ellas. El nuevo comando sería similar a este:

Listado de Código 1.10:

$ sed -e 's/<[^>]*>//g' miarchivo.html

En el ejemplo anterior, el '[^>]' especifica un "carácter que no sea '>'" y el '*' después del mismo completa la expresión para significar "cero o más caracteres que no sean '>'". Recomiendo probar este comando con algunos archivos HTML de ejemplo, encauzarlos con more y comprobar los resultados.

Coincidencia con más caracteres

La sintaxis de expresiones regulares '[ ]' tiene más opciones adicionales. Para especificar un rango de caracteres se puede usar '-', siempre y cuando no esté ni en la primera ni en la última posición, como se muestra a continuación:

Listado de Código 1.11: Especificar un rango de caracteres

'[a-x]*'

Apuntará a cero o más caracteres, siempre que cada uno de ellos sea una 'a', 'b','c'...'v','w','x'. Además, la clase de caracteres '[:space:]' está disponible para coincidir con espacios en blanco. He aquí una lista bastante completa de clases de caracteres:

Clase de carácter Descripción
[:alnum:] Alfanumérico [a-z A-Z 0-9]
[:alpha:] Alfabético [a-z A-Z]
[:blank:] Espacios o tabuladores
[:cntrl:] Cualquier carácter de control
[:digit:] Dígitos numéricos [0-9]
[:graph:] Cualquier carácter visible (no espacios en blanco)
[:lower:] Minúsculas [a-z]
[:print:] Caracteres que no sean de control
[:punct:] Caracteres de puntuación
[:space:] Espacio en blanco
[:upper:] Mayúsculas [A-Z]
[:xdigit:] Dígitos hexadecimales [0-9 a-f A-F]

Es una gran ventaja usar clases de caracteres siempre que sea posible, dado que se adaptan mejor a las localizaciones no inglesas (incluyendo caracteres con acentos siempre que sea necesario, etc).

Asuntos de sustitución avanzada

Nos hemos detenido con la realización de simples e incluso complejas sustituciones directas, pero sed puede hacer mucho más. Podemos referirnos a partes o a cadenas enteras con las que coincida la expresión regular y usar estas partes para construir la cadena de reemplazo. Como ejemplo, digamos que estamos respondiendo a un mensaje. El siguiente ejemplo pondrá el prefijo "Rafa dijo: " a cada frase:

Listado de Código 1.12: Añadir un prefijo a cada frase con cierta cadena

$ sed -e 's/.*/Rafa dijo: &/' msjorig.txt

La salida será similar a esta:

Listado de Código 1.13: Salida del anterior comando

Rafa dijo: Hola Jaime,
Rafa dijo:
Rafa dijo: ¡Seguro que me gusta este material acerca de sed!
Rafa dijo:

En este ejemplo, usamos el carácter '&' en la cadena de reemplazo, que le indica a sed que inserte la expresión regular completa con la que coincida. Por lo que todo lo que coincidió con '.*' (el mayor grupo de cero o más caracteres en la línea, o la línea completa) puede ser introducido en la cadena de reemplazo, incluso en múltiples ocasiones. Esto está muy bien, pero sed es mucho más poderoso aún.

Aquellos maravillosos paréntesis con barras invertidas

Aún mejor que con '&', el comando s/// nos permite definir regiones en nuestra expresión regular, y podemos referirnos a estas regiones específicas en nuestra cadena de reemplazo. Como ejemplo, digamos que tenemos un archivo que contiene el siguiente texto:

Listado de Código 1.14: Cita del texto

foo bar oni
eeny meeny miny
larry curly moe
jimmy the weasel

Ahora, digamos que queremos escribir un archivo de comandos sed que reemplace "eeny meeny miny" con "Victor eeny-meeny Von miny", etc. Para hacerlo, primero deberíamos escribir una expresión regular con la que coincidan las tres cadenas, separadas por espacios:

Listado de Código 1.15: Coincidir con la expresión regular

'.* .* .*'

Aquí está. Ahora, definiremos regiones insertando paréntesis con barras invertidas alrededor de cada región de nuestro interés:

Listado de Código 1.16: Definición de regiones

'\(.*\) \(.*\) \(.*\)'

Esta expresión regular funcionará exactamente igual que la anterior, excepto porque definirá tres regiones lógicas a las que podremos referirnos en nuestra cadena de reemplazo. He aquí el archivo de comandos final:

Listado de Código 1.17: Archivo de comandos final

$ sed -e 's/\(.*\) \(.*\) \(.*\)/Victor \1-\2 Von \3/' miarchivo.txt

Como puede verse, nos referimos a cada región delimitada por un paréntesis tecleando '\x', donde x es el número de región, comenzando por uno. El resultado será el siguiente:

Listado de Código 1.18: Resultado del anterior comando

Victor foo-bar Von oni
Victor eeny-meeny Von miny
Victor larry-curly Von moe
Victor jimmy-the Von weasel

A medida que uno se familiariza con sed, puede realizarse un procesamiento de textos realmente poderoso con un mínimo de esfuerzo. Pensemos ahora en cómo habríamos afrontado este mismo problema con nuestro lenguaje para crear archivos de comandos favorito -- ¿habríamos sido capaces de encontrar la solución con una sola línea?

Combinarlo todo

A medida que creamos archivos de comandos con sed más complejos, necesitaremos la capacidad para introducir más de un comando. Hay varias formas de lograrlo. Primero podemos usar puntos y comas entre los comandos. Por ejemplo, en esta serie de comandos se emplea el comando '=', que le indica a sed que muestre el número de línea, así como el comando p, que le indica a sed que muestre la línea (dado que estamos en el modo '-n'):

Listado de Código 1.19: Primer método, puntos y comas

$ sed -n -e '=;p' myfile.txt

Cada vez que se indiquen dos o más comandos, se ejecutará cada comando (en orden) para cada línea del archivo. En el ejemplo anterior, primero se aplica el comando '=' a la línea 1, y después se le aplica el comando p. Entonces sed procede con la línea 2 y repite el proceso. Mientras que los puntos y comas son muy útiles, hay ciertas situaciones en las que no funcionará. Otra alternativa sería usar dos opciones -e para especificar dos comandos separados:

Listado de Código 1.20: Segundo método, múltiples -e

$ sed -n -e '=' -e 'p' miarchivo.txt

De todas formas, cuando nos adentremos en comandos más complejos para añadir e insertar texto, no será suficiente con múltiples opciones '-e'. Para archivos de comandos complejos con varias líneas, el método más adecuado es poner todos esos comandos en un archivo diferente. Después haremos referencia a este archivo de comandos con la opción -f:

Listado de Código 1.21: Tercer método, archivo externo con comandos

$ sed -n -f miscomandos.sed miarchivo.txt

Este método, aunque parezca menos conveniente, siempre funcionará.

Múltiples comandos en una sola dirección

A veces, podemos querer especificar múltiples comandos que se apliquen en una sola dirección. Lo cual es realmente útil cuando se realizan montones de s/// para modificar palabras o la sintaxis de un archivo. Para realizar múltiples comandos en una sola dirección, introducimos nuestros comandos sed en un archivo y usamos los caracteres de llaves '{ }' para agruparlos, como sigue:

Listado de Código 1.22: Introducción de múltiples comandos por dirección

1,20{
        s/[Ll]inux/GNU\/Linux/g
        s/samba/Samba/g
        s/posix/POSIX/g
}

El ejemplo anterior aplicará tres comandos de sustitución desde la línea 1 a la 20, inclusive. Se pueden expresar también direcciones con expresiones regulares o una combinación de ambas:

Listado de Código 1.23: Combinación de ambos métodos

1,/^END/{
        s/[Ll]inux/GNU\/Linux/g
        s/samba/Samba/g
        s/posix/POSIX/g
       p
}

Este ejemplo aplicará todos los comandos entre '{ }' a las líneas que se encuentren entre la primera y aquella que comience con las letras "END", o hasta el final del archivo, si no se encuentra "END" antes.

Añadir, insertar y cambiar de línea

Ahora que estamos escribiendo archivos de comandos sed en archivos separados, podemos aprovechar los comandos para añadir, insertar y cambiar de línea. Estos comandos insertarán una línea después de la línea actual, insertarán una línea antes de la línea actual o reemplazarán a la línea actual en el espacio de patrones. Pueden usarse para insertar múltiples líneas en su salida. El comando para insertar líneas se usa como sigue:

Listado de Código 1.24: Usar el comando para insertar líneas

i\
Esta línea se insertará antes de cada línea

Si no se especifica ninguna dirección en la que aplicar este comando, se aplicará a cada línea y producirá un resultado como el siguiente:

Listado de Código 1.25: Resultados del anterior comando

Esta línea se insertará antes de cada línea
línea 1 aquí
Esta línea se insertará antes de cada línea
línea 2 aquí
Esta línea se insertará antes de cada línea
línea 3 aquí
Esta línea se insertará antes de cada línea
línea 4 aquí

Si se quieren insertar varias líneas antes de la actual, se pueden añadir agregando una barra invertida a la línea anterior, como se muestra:

Listado de Código 1.26: Insertar varias líneas antes de la actual

i\
inserta esta línea\
y esta otra\
y esta otra\
y, ¡ah!, esta también.

El comando para añadir funciona de forma similar, pero insertará una línea o líneas después de la actual en el espacio de patrones. Se usa como sigue:

Listado de Código 1.27: Añadir líneas después de la actual

a\
Añade esta línea después de cada línea. ¡Gracias! :)

Por otra parte, el comando para cambiar de línea reemplazará la línea actual en el espacio de patrones, y se usa como se indica:

Dado que el comando para añadir, insertar y cambiar de línea necesitan ser indicados en varias líneas, es necesario teclearlos en archivos de comandos de texto e indicar a sed que los ejecute con la opción '-f'. Usar los otros métodos de sed para introducir comandos resultará problemático.

La próxima vez

La próxima vez, en el último artículo acerca de sed en esta serie, mostraré muchos ejemplos excelentes de uso en el mundo real para muchos tipos diferentes de tareas. No solamente mostraré lo que los archivos de comandos hacen sino porqué lo hacen. Después de consultarlo, dispondremos de muchas ideas adicionales excelentes acerca de cómo usar sed en nuestros proyectos. ¡Nos vemos !

2.  Recursos

Enlaces útiles



Imprimir

Página actualizada 9 de octubre, 2005

Sumario: Sed es un poderoso y compacto editor de flujos de texto. En este artículo, el segundo de la serie, Daniel muestra cómo realizar sustituciones de cadenas; crear archivos de comandos más amplios y usar los comandos de sed para añadir, insertar y modificar una línea.

Daniel Robbins
Autor

LinuxBlues
Traductor

Donate to support our development efforts.

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