- Dave McKay
@TheGurkha
- 16 de abril de 2020, 8:00am EDT
Puede parecer una locura, pero el comando Linux sed
es un editor de texto sin interfaz. Puedes usarlo desde la línea de comandos para manipular texto en archivos y flujos. Te mostraremos cómo aprovechar su poder.
El poder de sed
El comando sed
es un poco como el ajedrez: se necesita una hora para aprender los fundamentos y toda una vida para dominarlos (o, al menos, mucha práctica). Le mostraremos una selección de gambitos de apertura en cada una de las principales categorías de funcionalidad de sed
.
sed
es un editor de flujos que funciona con entradas o archivos de texto canalizados. Sin embargo, no tiene una interfaz de editor de texto interactivo. Más bien, usted proporciona instrucciones para que siga mientras trabaja a través del texto. Todo esto funciona en Bash y otros shells de línea de comandos.
Con sed
puedes hacer todo lo siguiente:
- Seleccionar texto
- Sustituir texto
- Añadir líneas al texto
- Borrar líneas del texto
- Modificar (o conservar) un archivo original
Hemos estructurado nuestros ejemplos para introducir y demostrar conceptos, no para producir los comandos más tersos (y menos accesibles) sed
. Sin embargo, las funcionalidades de coincidencia de patrones y selección de texto de sed
se basan en gran medida en expresiones regulares (regexes). Vas a necesitar cierta familiaridad con ellas para sacar lo mejor de sed
.
Relacionado: Cómo usar expresiones regulares (regexes) en Linux
Un ejemplo sencillo
Primero, vamos a usar echo
para enviar algún texto a sed
a través de una tubería, y hacer que sed
sustituya una parte del texto. Para ello, escribimos lo siguiente:
echo howtogonk | sed 's/gonk/geek/'
El comando echo
envía «howtogonk» a sed
, y se aplica nuestra sencilla regla de sustitución (la «s» significa sustitución). sed
busca en el texto de entrada una ocurrencia de la primera cadena, y reemplazará cualquier coincidencia con la segunda.
La cadena «gonk» se sustituye por «geek», y la nueva cadena se imprime en la ventana del terminal.
Las sustituciones son probablemente el uso más común de sed
. Sin embargo, antes de que podamos profundizar en las sustituciones, necesitamos saber cómo seleccionar y emparejar texto.
Seleccionar texto
Vamos a necesitar un archivo de texto para nuestros ejemplos. Utilizaremos uno que contiene una selección de versos del poema épico de Samuel Taylor Coleridge «The Rime of the Ancient Mariner».»
Escribimos lo siguiente para echarle un vistazo con less
:
less coleridge.txt
Para seleccionar algunas líneas del archivo, proporcionamos las líneas de inicio y final del rango que queremos seleccionar. Un solo número selecciona esa línea.
Para extraer las líneas uno a cuatro, escribimos este comando:
sed -n '1,4p' coleridge.txt
Nota la coma entre 1
y 4
. El p
significa «imprimir líneas coincidentes». Por defecto, sed
imprime todas las líneas. Veríamos todo el texto del archivo con las líneas coincidentes impresas dos veces. Para evitarlo, utilizaremos la opción -n
(quiet) para suprimir el texto no coincidente.
Cambiamos los números de línea para poder seleccionar un verso diferente, como se muestra a continuación:
sed -n '6,9p' coleridge.txt
Podemos utilizar la opción -e
(expresión) para realizar selecciones múltiples. Con dos expresiones, podemos seleccionar dos versos, así:
sed -n -e '1,4p' -e '31,34p' coleridge.txt
Si reducimos el primer número de la segunda expresión, podemos insertar un espacio en blanco entre los dos versos. Escribimos lo siguiente:
sed -n -e '1,4p' -e '30,34p' coleridge.txt
También podemos elegir una línea inicial y decirle a sed
que recorra el archivo e imprima líneas alternas, cada cinco líneas, o que se salte cualquier número de líneas. El comando es similar a los que utilizamos anteriormente para seleccionar un rango. Esta vez, sin embargo, utilizaremos una tilde (~
) en lugar de una coma para separar los números.
El primer número indica la línea inicial. El segundo número indica sed
qué líneas después de la línea de salida queremos ver. El número 2 significa cada segunda línea, el 3 significa cada tercera línea, y así sucesivamente.
Escribimos lo siguiente:
sed -n '1~2p' coleridge.txt
No siempre sabrás dónde se encuentra el texto que buscas en el archivo, lo que significa que los números de línea no siempre serán de gran ayuda. Sin embargo, también puedes utilizar sed
para seleccionar las líneas que contienen patrones de texto que coinciden. Por ejemplo, extraigamos todas las líneas que empiecen por «Y».
El signo de intercalación (^
) representa el inicio de la línea. Encerraremos nuestro término de búsqueda entre barras inclinadas (/
). También incluimos un espacio después de «Y» para que palabras como «Android» no se incluyan en el resultado.
La lectura de los scripts sed
puede ser un poco difícil al principio. El /p
significa «imprimir», al igual que en los comandos que hemos utilizado anteriormente. En el siguiente comando, sin embargo, una barra diagonal le precede:
sed -n '/^And /p' coleridge.txt
Tres líneas que empiezan por «And » se extraen del archivo y se nos muestran.
Hacer sustituciones
En nuestro primer ejemplo, te mostramos el siguiente formato básico para una sed
sustitución:
echo howtogonk | sed 's/gonk/geek/'
El s
indica a sed
que se trata de una sustitución. La primera cadena es el patrón de búsqueda, y la segunda es el texto con el que queremos sustituir ese texto coincidente. Por supuesto, como con todas las cosas de Linux, el diablo está en los detalles.
Escribimos lo siguiente para cambiar todas las ocurrencias de «día» a «semana», y dar al marino y al albatros más tiempo para vincularse:
sed -n 's/day/week/p' coleridge.txt
En la primera línea, sólo se cambia la segunda ocurrencia de «día». Esto se debe a que sed
se detiene después de la primera coincidencia por línea. Tenemos que añadir una «g» al final de la expresión, como se muestra a continuación, para realizar una búsqueda global y que se procesen todas las coincidencias de cada línea:
sed -n 's/day/week/gp' coleridge.txt
Esto coincide con tres de las cuatro de la primera línea. Como la primera palabra es «Día», y sed
distingue entre mayúsculas y minúsculas, no considera que esa instancia sea la misma que «día.»
Escribimos lo siguiente, añadiendo un i
al comando al final de la expresión para indicar la insensibilidad a las mayúsculas y minúsculas:
sed -n 's/day/week/gip' coleridge.txt
Esto funciona, pero puede que no siempre quieras activar la insensibilidad a las mayúsculas y minúsculas para todo. En esos casos, puedes utilizar un grupo regex para añadir la insensibilidad a las mayúsculas y minúsculas específica del patrón.
Por ejemplo, si encerramos los caracteres entre corchetes (), se interpretan como «cualquier carácter de esta lista de caracteres.»
Escribimos lo siguiente, e incluimos «D» y «d» en el grupo, para asegurarnos de que coincide tanto con «Day» como con «day»:
sed -n 's/ay/week/gp' coleridge.txt
También podemos restringir las sustituciones a secciones del archivo. Digamos que nuestro archivo contiene un espaciado extraño en el primer verso. Podemos usar el siguiente comando conocido para ver el primer verso:
sed -n '1,4p' coleridge.txt
Buscaremos dos espacios y los sustituiremos por uno. Lo haremos de forma global para que la acción se repita en toda la línea. Para que quede claro, el patrón de búsqueda es espacio, espacio asterisco (*
), y la cadena de sustitución es un solo espacio. El 1,4
restringe la sustitución a las cuatro primeras líneas del archivo.
Juntamos todo eso en el siguiente comando:
sed -n '1,4 s/ */ /gp' coleridge.txt
¡Esto funciona bien! El patrón de búsqueda es lo importante aquí. El asterisco (*
) representa cero o más del carácter precedente, que es un espacio. Por lo tanto, el patrón de búsqueda está buscando cadenas de un espacio o más.
Si sustituimos un solo espacio por cualquier secuencia de espacios múltiples, devolveremos el archivo al espaciado regular, con un solo espacio entre cada palabra. Esto también sustituirá un solo espacio por un solo espacio en algunos casos, pero esto no afectará negativamente, seguiremos obteniendo nuestro resultado deseado.
Si escribimos lo siguiente y reducimos el patrón de búsqueda a un solo espacio, verás inmediatamente por qué tenemos que incluir dos espacios:
sed -n '1,4 s/ */ /gp' coleridge.txt
Debido a que el asterisco coincide con cero o más del carácter precedente, ve cada carácter que no es un espacio como un «espacio cero» y le aplica la sustitución.
Sin embargo, si incluimos dos espacios en el patrón de búsqueda, sed
debe encontrar al menos un carácter de espacio antes de aplicar la sustitución. Esto asegura que los caracteres que no son espacios no serán tocados.
Escribimos lo siguiente, utilizando la -e
(expresión) que hemos utilizado antes, que nos permite hacer dos o más sustituciones simultáneamente:
sed -n -e 's/motion/flutter/gip' -e 's/ocean/gutter/gip' coleridge.txt
Podemos conseguir el mismo resultado si utilizamos un punto y coma (;
) para separar las dos expresiones, así:
sed -n 's/motion/flutter/gip;s/ocean/gutter/gip' coleridge.txt
Cuando intercambiamos «day» por «week» en el siguiente comando, la instancia de «day» en la expresión «well a-day» se intercambió también:
sed -n 's/ay/week/gp' coleridge.txt
Para evitar esto, sólo podemos intentar sustituciones en líneas que coincidan con otro patrón. Si modificamos el comando para que tenga un patrón de búsqueda al principio, sólo consideraremos operar sobre las líneas que coincidan con ese patrón.
Escribimos lo siguiente para que nuestro patrón coincidente sea la palabra «after»:
sed -n '/after/ s/ay/week/gp' coleridge.txt
Eso nos da la respuesta que queremos.
Más sustituciones complejas
Demos un respiro a Coleridge y utilicemos sed
para extraer nombres del archivo etc/passwd
.
Hay formas más cortas de hacer esto (más adelante), pero aquí usaremos la forma más larga para demostrar otro concepto. Cada elemento coincidente en un patrón de búsqueda (llamado subexpresiones) puede ser numerado (hasta un máximo de nueve elementos). A continuación, puede utilizar estos números en sus comandos sed
para hacer referencia a subexpresiones específicas.
Tiene que encerrar la subexpresión entre paréntesis para que esto funcione. Los paréntesis también deben estar precedidos por una barra invertida (\
) para evitar que sean tratados como un carácter normal.
Para hacer esto, escribirías lo siguiente:
sed 's/\(*\).*/\1/' /etc/passwd
Desglosemos esto:
-
sed 's/
: El comandosed
y el comienzo de la expresión de sustitución. -
\(
: El paréntesis de apertura que encierra la subexpresión, precedido por una barra invertida (\
). -
*
: La primera subexpresión del término de búsqueda contiene un grupo entre corchetes. El signo de intercalación (^
) significa «no» cuando se utiliza en un grupo. Un grupo significa que cualquier carácter que no sea dos puntos (:
) se aceptará como coincidencia. -
\)
: El paréntesis de cierre con una barra invertida precedente (\
). -
.*
: Esta segunda subexpresión de búsqueda significa «cualquier carácter y cualquier número de ellos.» -
/\1
: La parte de sustitución de la expresión contiene1
precedida de una barra invertida (\
). Esto representa el texto que coincide con la primera subexpresión. -
/'
: La barra diagonal de cierre (/
) y las comillas simples ('
) terminan el comandosed
.
Lo que todo esto significa es que vamos a buscar cualquier cadena de caracteres que no contenga dos puntos (:
), que será la primera instancia de texto coincidente. Luego, buscamos cualquier otra cosa en esa línea, que será la segunda instancia de texto coincidente. Vamos a sustituir toda la línea con el texto que coincida con la primera subexpresión.
Cada línea del archivo /etc/passwd
comienza con un nombre de usuario terminado en dos puntos. Hacemos coincidir todo hasta los primeros dos puntos, y luego sustituimos ese valor por toda la línea. Así, hemos aislado los nombres de usuario.
A continuación, encerraremos la segunda subexpresión entre paréntesis para poder referenciarla también por número. También sustituiremos \1
por \2
. Nuestro comando sustituirá ahora toda la línea con todo lo que hay desde los primeros dos puntos (:
) hasta el final de la línea.
Escribimos lo siguiente:
sed 's/\(*\)\(.*\)/\2/' /etc/passwd
Estos pequeños cambios invierten el significado del comando, y obtenemos todo excepto los nombres de usuario.
Ahora, echemos un vistazo a la forma rápida y fácil de hacer esto.
Nuestro término de búsqueda va desde los primeros dos puntos (:
) hasta el final de la línea. Como nuestra expresión de sustitución está vacía (//
), no sustituiremos el texto coincidente por nada.
Así que escribimos lo siguiente, cortando todo desde los primeros dos puntos (:
) hasta el final de la línea, dejando sólo los nombres de usuario:
sed 's/:.*//" /etc/passwd
Veamos un ejemplo en el que referenciamos la primera y la segunda coincidencia en el mismo comando.
Tenemos un archivo de comas (,
) separando nombres y apellidos. Queremos listarlos como «apellido, nombre». Podemos usar cat
, como se muestra a continuación, para ver lo que hay en el archivo:
cat geeks.txt
Como muchos de los comandos sed
, este siguiente puede parecer impenetrable al principio:
sed 's/^\(.*\),\(.*\)$/\2,\1 /g' geeks.txt
Este es un comando de sustitución como los otros que hemos utilizado, y el patrón de búsqueda es bastante sencillo. Lo desglosaremos a continuación:
-
sed 's/
: El comando de sustitución normal. -
^
: Como el signo de intercalación no está en un grupo (), significa «El inicio de la línea.»
-
\(.*\),
: La primera subexpresión es un número cualquiera de caracteres. Está encerrada entre paréntesis , cada uno de los cuales está precedido por una barra invertida (\
) para que podamos referenciarla por número. Todo nuestro patrón de búsqueda hasta ahora se traduce como búsqueda desde el inicio de la línea hasta la primera coma (,
) para cualquier número de cualquier carácter. -
\(.*\)
: La siguiente subexpresión es (de nuevo) cualquier número de cualquier carácter. También está encerrada entre paréntesis , ambos precedidos por una barra invertida (\
) para que podamos referenciar el texto coincidente por número. -
$/
: El signo de dólar ($
) representa el final de la línea y permitirá que nuestra búsqueda continúe hasta el final de la línea. Hemos utilizado esto simplemente para introducir el signo de dólar. Realmente no lo necesitamos aquí, ya que el asterisco (*
) iría al final de la línea en este escenario. La barra diagonal (/
) completa la sección del patrón de búsqueda. -
\2,\1 /g'
: Como hemos encerrado nuestras dos subexpresiones entre paréntesis, podemos referirnos a ambas por sus números. Como queremos invertir el orden, las escribimos comosecond-match,first-match
. Los números tienen que ir precedidos de una barra invertida (\
). -
/g
: Esto permite que nuestro comando funcione globalmente en cada línea. -
geeks.txt
: El archivo en el que estamos trabajando.
También puedes utilizar el comando Cortar (c
) para sustituir líneas enteras que coincidan con tu patrón de búsqueda. Escribimos lo siguiente para buscar una línea con la palabra «cuello» en ella, y la sustituimos por una nueva cadena de texto:
sed '/neck/c Around my wrist was strung' coleridge.txt
Nuestra nueva línea aparece ahora en la parte inferior de nuestro extracto.
Insertar líneas y texto
También podemos insertar nuevas líneas y texto en nuestro archivo. Para insertar nuevas líneas después de las que coincidan, utilizaremos el comando Append (a
).
Este es el archivo con el que vamos a trabajar:
cat geeks.txt
Hemos numerado las líneas para que sea un poco más fácil de seguir.
Escribimos lo siguiente para buscar las líneas que contengan la palabra «He», e insertar una nueva línea debajo de ellas:
sed '/He/a --> Inserted!' geeks.txt
Escribimos lo siguiente e incluimos el comando Insert (i
) para insertar la nueva línea por encima de las que contienen texto coincidente:
sed '/He/i --> Inserted!' geeks.txt
Podemos utilizar el ampersand (&
), que representa el texto original coincidente, para añadir nuevo texto a una línea coincidente. \1
\2
, y así sucesivamente, representan subexpresiones coincidentes.
Para añadir texto al principio de una línea, utilizaremos un comando de sustitución que coincida con todo lo que hay en la línea, combinado con una cláusula de sustitución que combine nuestro nuevo texto con la línea original.
Para hacer todo esto, escribimos lo siguiente:
sed 's/.*/--> Inserted &/' geeks.txt
Escribimos lo siguiente, incluyendo el comando G
, que añadirá una línea en blanco entre cada línea:
sed 'G' geeks.txt
Si quieres añadir dos o más líneas en blanco, puedes usar G;G
G;G;G
, y así sucesivamente.
Borrar líneas
El comando Borrar (d
) borra las líneas que coinciden con un patrón de búsqueda, o las especificadas con números de línea o rangos.
Por ejemplo, para borrar la tercera línea, escribiríamos lo siguiente:
sed '3d' geeks.txt
Para borrar el rango de líneas cuatro a cinco, escribiríamos lo siguiente:
sed '4,5d' geeks.txt
Para borrar las líneas fuera de un rango, utilizamos un signo de exclamación (!
), como se muestra a continuación:
sed '6,7!d' geeks.txt
Guardando tus cambios
Hasta ahora, todos nuestros resultados se han impreso en la ventana de terminal, pero aún no los hemos guardado en ningún sitio. Para hacerlos permanentes, puede escribir sus cambios en el archivo original o redirigirlos a uno nuevo.
Sobreescribir su archivo original requiere cierta precaución. Si su comando sed
es erróneo, podría hacer algunos cambios en el archivo original que son difíciles de deshacer.
Para cierta tranquilidad, sed
puede crear una copia de seguridad del archivo original antes de ejecutar su comando.
Puedes usar la opción In-place (-i
) para decirle a sed
que escriba los cambios en el archivo original, pero si le añades una extensión de archivo, sed
hará una copia de seguridad del archivo original en uno nuevo. Tendrá el mismo nombre que el archivo original, pero con una nueva extensión de archivo.
Para demostrarlo, buscaremos cualquier línea que contenga la palabra «He» y la borraremos. También haremos una copia de seguridad de nuestro archivo original en uno nuevo utilizando la extensión BAK.
Para hacer todo esto, escribimos lo siguiente:
sed -i'.bak' '/^.*He.*$/d' geeks.txt
Escribimos lo siguiente para asegurarnos de que nuestro archivo de copia de seguridad no se modifica:
cat geeks.txt.bak
También podemos escribir lo siguiente para redirigir la salida a un nuevo fichero y conseguir un resultado similar:
sed -i'.bak' '/^.*He.*$/d' geeks.txt > new_geeks.txt
Usamos cat
para confirmar que los cambios se han escrito en el nuevo archivo, como se muestra a continuación:
cat new_geeks.txt
Tener sed todo eso
Como probablemente habrás notado, incluso este rápido manual sobre sed
es bastante largo. Hay mucho en este comando, y hay aún más que puede hacer con él.
Con suerte, sin embargo, estos conceptos básicos han proporcionado una base sólida sobre la que se puede construir a medida que continúe aprendiendo más.
Dave McKay utilizó por primera vez los ordenadores cuando la cinta de papel perforado estaba de moda, y ha estado programando desde entonces. Después de más de 30 años en la industria de las tecnologías de la información, ahora es un periodista tecnológico a tiempo completo. A lo largo de su carrera, ha trabajado como programador independiente, director de un equipo internacional de desarrollo de software, gestor de proyectos de servicios informáticos y, más recientemente, como responsable de protección de datos. Dave es un evangelista de Linux y defensor del código abierto.Read Full Bio »