sed: Editando texto desde línea de comandos

Publicado por Diego Córdoba en

Hoy veremos una intro práctica a la herramienta sed de línea de comandos, material obligatorio del curso de LPIC-1, ya que es uno de los requisitos para obtener dicha certificación.

Una interesante herramienta de línea de comandos en GNU/Linux es el comando sed (Stream EDitor). Nos sirve para editar texto de manera automatizada sin necesidad de usar un editor de texto. sed, como un editor de flujo, permite modificar todo el texto que recibe mediante un archivo o salida de un pipe («|«), y hace uso de comandos especiales para realizar cambios en el flujo de texto que recibe.

Los cambios que deseamos realizar sobre un texto ya debemos tenerlos planificados, y al pedirle a sed que los implemente, sed lo hace con una sola pasada sobre el texto en cuestión, lo que lo hace, además, eficiente.

¿Cómo funciona sed?

El editor sed cambia los datos basándose en comandos especificados tanto por línea de comandos como mediante un archivo de comandos predefinido. El proceso de edición sigue una secuencia particular:

  1. Lee una línea de texto desde el flujo de entrada.
  2. Verifica si la línea coincide con los comandos suministrados a sed.
  3. Modifica el texto tal y como se ha especificado en dichos comandos.
  4. Muestra el texto modificado.

Después de que sed realiza este procedimiento con una línea de texto de entrada, para todos los comandos especificados, pasa a la siguiente línea.

Una vez que sed haya alcanzado el final del archivo o del flujo de entrada, se detiene.

Sintaxis y ejemplos de sed

La sintaxis básica de sed es la siguiente:

sed [OPCIONES] [SCRIPT]... [ARCHIVO]

Por defecto sed utiliza la entrada estándar para leer el flujo de texto a modificar de acuerdo a los comandos, por lo que podemos usar el «|» para enviarle datos.

Nada mejor que una serie de ejemplos para entender su funcionamiento.

Modificando texto desde STDIN

$ echo "odio aprender sed" | sed 's/odio/quiero/'
quiero aprender sed

En este ejemplo tomamos la línea de entrada «odio aprender sed» y cambiamos la palabra «odio» por «quiero», obteniendo la salida correspondiente.

En este caso sed modifica un flujo de entrada enviado por pipe desde el comando echo.

En este ejemplo utilizamos el comando s (substitute) para reemplazar la primer ocurrencia de la palabra buscada con su sustitución.

Hay que tener en cuenta que esto modifica, como dije, una sola ocurrencia… por lo que, si en el texto de ejemplo, la palabra «odio» se repite, sólo se modificará la primera ocurrencia:

$ echo "odio aprender sed, realmente lo odio" | sed 's/odio/quiero/'
quiero aprender sed, realmente lo odio

Para poder reemplazar todas las ocurrencias debemos añadir el modificador g (global) al final de la línea de comandos:

$ echo "odio aprender sed, realmente lo odio" | sed 's/odio/quiero/g'
quiero aprender sed, realmente lo quiero

Modificando texto de un archivo

Supongamos ahora que tenemos un archivo de texto con el contenido que queremos analizar y modificar. Supongamos que tenemos el siguiente archivo (es el sources.list de uno de mis servidores Debian):

deb http://ftp.us.debian.org/debian/ bullseye main contrib non-free
deb-src http://security.debian.org bullseye/updates main
deb http://mirrors.digitalocean.com/debian bullseye-updates main
deb-src http://mirrors.digitalocean.com/debian bullseye-updates main
deb https://deb.debian.org/debian-security bullseye-security main contrib
deb-src https://deb.debian.org/debian-security bullseye-security main contrib
deb http://mirrors.digitalocean.com/debian bullseye-backports main
deb-src http://mirrors.digitalocean.com/debian bullseye-backports main

Y, por alguna razón, necesitamos cambiar las palabras «bullseye» por, supongamos, «stable«.

Podemos hacer lo siguiente:

$ sed 's/bullseye/stable/g' /etc/apt/sources.list
deb http://ftp.us.debian.org/debian/ stable main contrib non-free
deb-src http://security.debian.org stable/updates main
deb http://mirrors.digitalocean.com/debian stable-updates main
deb-src http://mirrors.digitalocean.com/debian stable-updates main
deb https://deb.debian.org/debian-security stable-security main contrib
deb-src https://deb.debian.org/debian-security stable-security main contrib
deb http://mirrors.digitalocean.com/debian stable-backports main
deb-src http://mirrors.digitalocean.com/debian stable-backports main
deb [arch=amd64] https://download.docker.com/linux/debian stable

Debemos tener en cuenta que esta salida representa el contenido del archivo modificado y escrito en salida estándar, es decir, el archivo original no se ha modificado. sed solamente modifica lo que recibe de entrada y envía a la salida estándar el contenido modificado. Podemos redireccionar la salida de sed para escribir un nuevo archivo, por ejemplo:

$ sed 's/bullseye/stable/g' /etc/apt/sources.list > /tmp/sources.list.nuevo

Eliminando líneas de texto

Supongamos que en el ejemplo anterior deseamos eliminar la línea del archivo que contiene la cadena «amd64«. Podemos usar el comando d para ello, de esta forma:

$ sed '/amd64/d' /etc/apt/sources.list
deb http://ftp.us.debian.org/debian/ bullseye main contrib non-free
deb-src http://security.debian.org bullseye/updates main
deb http://mirrors.digitalocean.com/debian bullseye-updates main
deb-src http://mirrors.digitalocean.com/debian bullseye-updates main
deb https://deb.debian.org/debian-security bullseye-security main contrib
deb-src https://deb.debian.org/debian-security bullseye-security main contrib
deb http://mirrors.digitalocean.com/debian bullseye-backports main
deb-src http://mirrors.digitalocean.com/debian bullseye-backports main

Cambiando líneas completas

Supongamos ahora que deseamos reemplazar la segunda línea del archivo por otra con otro contenido diferente. Podemos hacerlo usando la sintaxis NLINEAcCONTENIDO, donde NLINEA representa el número de línea dentro del archivo, y CONTENIDO el nuevo contenido de dicha línea.

$ sed '2c#esto es un comentario reemplazando la linea 2' /tmp/sources.list
deb http://ftp.us.debian.org/debian/ bullseye main contrib non-free
#esto es un comentario reemplazando la linea 2
deb http://mirrors.digitalocean.com/debian bullseye-updates main
deb-src http://mirrors.digitalocean.com/debian bullseye-updates main
deb https://deb.debian.org/debian-security bullseye-security main contrib
deb-src https://deb.debian.org/debian-security bullseye-security main contrib
deb http://mirrors.digitalocean.com/debian bullseye-backports main
deb-src http://mirrors.digitalocean.com/debian bullseye-backports main
deb [arch=amd64] https://download.docker.com/linux/debian bullseye

Realizando varios cambios a la vez con sed

Supongamos ahora que deseamos realizar todos los cambios aplicados anteriormente, pero en un solo comando. Veamos cómo podemos usar la opción «-e» para realizar los siguientes cambios:

  • Cambiar la palabra «bullseye» por «stable».
  • Eliminar la línea que contiene la cadena «amd64»
  • Reemplazar la línea 2 con un comentario.
$ sed -e 's/bullseye/stable/g ; /amd64/d ; 2c#esto es un comentario reemplazando la linea 2' /tmp/sources.list
deb http://ftp.us.debian.org/debian/ stable main contrib non-free
#esto es un comentario reemplazando la linea 2
deb http://mirrors.digitalocean.com/debian stable-updates main
deb-src http://mirrors.digitalocean.com/debian stable-updates main
deb https://deb.debian.org/debian-security stable-security main contrib
deb-src https://deb.debian.org/debian-security stable-security main contrib
deb http://mirrors.digitalocean.com/debian stable-backports main
deb-src http://mirrors.digitalocean.com/debian stable-backports main

Aquí se ve que el comando usa el modificador -e para especificar, entre comillas, todos los comandos que deseamos aplicar, en orden, al archivo, separados por punto y coma «;«.

Sí, si son muchos los comandos que debemos aplicar a un texto de entrada, especificarlo así puede ser engorroso… así que sed pensó en todo, podemos agrupar los comandos en un archivo de comandos, y luego decirle a sed que aplique sobre el texto todos los comandos especificados en el archivo.

Supongamos los comandos anteriores, escritos en un archivo, línea por línea. Llamémosle comandos.sed:

s/bullseye/stable/g
/amd64/d
2c#esto es un comentario reemplazando la linea 2

Ahora podemos usar la opción -f o --file= para indicarle a sed dónde está el archivo de comandos a aplicar:

$ sed -f /tmp/comandos.sed /etc/apt/sources.list
deb http://ftp.us.debian.org/debian/ stable main contrib non-free
#esto es un comentario reemplazando la linea 2
deb http://mirrors.digitalocean.com/debian stable-updates main
deb-src http://mirrors.digitalocean.com/debian stable-updates main
deb https://deb.debian.org/debian-security stable-security main contrib
deb-src https://deb.debian.org/debian-security stable-security main contrib
deb http://mirrors.digitalocean.com/debian stable-backports main
deb-src http://mirrors.digitalocean.com/debian stable-backports main
sed script example

Conclusiones

Hemos llegado al final de esta intro al comando sed!

Es un comando bastante amplio, algo muy interesante que puede agregarse, que no sumé en este post para no hacerlo muy extenso, es el uso de expresiones regulares mediante el modificador -r o --regexp-extended, que nos permiten aplicar filtros más complejos.

Para más modificadores, siempre recuerden que tienen el man sed 🙂

Este post cubre uno de los temas necesarios para certificar LPIC-1 (exámenes 101 y 102), por lo que es parte del material de lectura del curso correspondiente.

Espero que les sea útil!

Hasta la próxima!


¿Preguntas? ¿Comentarios?

Si tenés dudas, o querés dejarnos tus comentarios y consultas, sumate al grupo de Telegram de la comunidad JuncoTIC!
¡Te esperamos!

Categorías: AplicacionesLinux

Diego Córdoba

- Ingeniero en Informática - Mg. Teleinformática - Tesis pendiente - Docente universitario - Investigador