¿Como uso el comando sed en Texto-plano?
Puede sonar extrano, pero sed es realmente un editor de texto sin interfaz alguna. Estaba pensado para el uso de terminales teletipo. Sin mebargo, podras utilizarlo desde tu terminal remota y a traves de tu interprete de comandos de texto-plano.xyz para manupular texto, tanto en ficheros como con cadenas. ¡Con este tutorial aprenderas a dominar su inusitado poder! El Poder de sed
El aprendizaje del comando sed se asemeja al ajedrez: lleva una ahora aprenderlo, y una vida dominarlo (o, al menos, debe practicarselo mucho). Con este tutorial aprenderas lo mismo que una apertura o gambito para cada una de las funcionalidades principales de sed.
sed es un editor de cadenas capaz de operar introduciendole entrada en forma de cadenas (a traves de canos), o bien con ficheros de texto enteros. Como carece de una interfaz interactiva propia de un editor de texto modeno, deberas introducir comandos para que el interpretara y ejecutara en la medida que avanza sobre el texto. Esto tambien funciona en Bash y otros intepretes de linea de comandos.
Con sed, puedras:
- Seleccionar texto
- Sustituir texto
- Agregar lineas al texto
- Borrar lineas de un texto
- Modificar (o preservar) un fichero original
Se han estructurado estos ejemplos para presentar y demostrar conceptos, y no para producir los comandos de sed mas rigido (y mas complejos) posibles. Incluso asi, veras que en sed las funcionalidades de coincidencia de cadenas y seleccion de texto operan a traves del uso de expresiones regulares (regexes). Deberias familiarizarte con ellas para obtener lo mejor de sed. Un Ejemplo Simple
Primero vamos a usar echo para enviar algun texto a sed a traves de un cano, y haremos que sed sustituya una porcion del texto. Para hacerlo, ingresamos:
echo comoreprobar | sed 's/reprobar/aprobar/'
El comando echo genera una cadena de texto y el cano ("|") envia dicha la cadena "comoreprobar" a sed. Y a continuacion le ordena aplicar una regla de sustitucion simple (la "s" significa sustituir). sed buscara la cadena de texto especificado en el texto contenido en el fichero, y reemplazara cualquier coincidencia con la segunda cadena.
La cadena "reprobar" sera reemplazada por "aprobar", y la nueva cadena sera presentada como una salida en la terminal.
Si bien las sustituciones son probablemnete el empleo mas comun que se hace de sed, es conveniente tambien saber como seleccionar y hacer coincidir texto.
Seleccionar Texto
Utiliza un fichero de texto para nuestro ejemplo. Usa uno que contiene un poema gauchesco decimas de Martin Castro, "Hachando los Alambrados". Lo podran descargar con el comando:
wget caja.texto-plano.xyz/peron/hachando.txt
Ingresamos lo siguiente para mirarlo con el comando less:
less hachando.txt
Para seleccionar algunas lineas del fichero, proveemos la linea inicial y la linea final del rango de seleccion. Si usamos un numero aislado, solo seleccionaremos dicha linea.
Para extraer las diez lineas que componen la primer decima, ingresa este comando:
sed -n '3,12p' hachando.txt
Observa la coma entre 3 y 12. La p
significa "presenta
las lineas coincidentes". Si actuase por defecto, sed presentaria todas
las lineas. Veriamos todo el texto en el fichero, y las lineas
coincidentes resultarian impresas dos veces (repetidas). Para impedirlo,
emplea la opcion -n
("sin salida") para suprimir todas las
lineas no coincidente.
Ahora cambia los numeros de linea de modo que podamos seleccionar un verso diferente, como se indica:
sed -n '14,23p' hachando.txt
Puedes usar la opcion -e
("expresion") para realizar
selecciones multiples. Si usas dos expresiones, podras seleccionar dos
versos, tal como:
sed -n -e '3,12p' -e '14,23p' hachando.txt
Y si reduces el primer numero en la segunda expresion, podras insertar una linea en blanco entre los dos versos. Tipea lo siguiente:
sed -n -e '3,12p' -e '13,23p' hachando.txt
Tambien puedes escoger una linea de comienzo e indicarle a sed que
avance a lo largo del fichero e imprima las lineas alternadamente, cada
cinco lineas, o bien que saltee un numero dado de lineas. El comando es
similar a aquellos que empleamos anteriormente para seleccionar un
rango. Para ello debes utilizar un tilde (~
) en lugar de
una , para separar los numeros.
El primer numero indicara el comienzo de la linea. El segundo numero le indicara a sed que lineas luego de la linea de comienzo deseas ver. El numero 2 significa cada segunda linea, 3 significa cada tercer linea, y asi.
Ingresa lo siguiente:
sed -n '1~3p' hachando.txt
Normalmente no se conoce con precision donde se localiza exactamente un texto buscado, lo que implica que suele ser imposible proveer los numeros de linea. Sin embargo, podras usar sed para seleccionar las lineas que contienen la cadena de texto que buscas. Por ejemplo, prueba extraer todas las lineas que comienzan con "Y".
El circunflejo (^
) representa el comienzo de una linea.
Delimita tu termino a buscar en barras (/). Tambien incluiras un espacio
luego de "Y", de manera tal que las palabras como "Yo" no apareceran en
el resultado.
Al leer por primera ver guiones que incluyen sed, no suele resultar
obvio. Recordemos que en los comandos anteriores explique que
/p
significa "presentar", o bien "imprimir". Sin embargo,
ahora una barra la antecede, de esta manera:
sed -n '/^y /p' hachando.txt
Como resultado, quince lineas que comienzan con "y" seran extraidas del fichero y presentadas en la terminal.
Realizar Sustiticiones
En nuestro primer ejemplo, expuse el primer formato basico para usar la substitucion en sed:
echo comoreprobar | sed 's/reprobar/aprobar/'
La s
indica a sed que lo que viene a continuacion es una
sustitucion lineal. La primer cadena consistira en la cadena a buscar,
mientras que la segunda cadena sera el contenido quiere reemplazar. Por
supuesto, como sucede en BSD, el demonio esta en los detalles.
Ingresa entonces el siguiente comando para cambiar todas las apariciones de la cadena "donde" a "ande", y le daremos a Serapio una voz mas pampeana:
sed -n 's/donde/ande/p' hachando.txt
En la primer linea, solo se cambiara la segunda ocurrencia de
"donde". Eso es asi porque sed se detiene luego de encontrar la primer
coincidencia por cada linea. Si queremos realizar una busqueda global -
tal que se procesen todas las coincidencias existentes en cada linea del
fichero, debemos agregar un modificador g
, de la siguiente
manera:
sed -n 's/donde/ande/gp' hachando.txt
Esto encontrara "donde" y lo reemplazara con "ande". Pero si la palabra es "Donde" sed es sensible a las mayusculas, y no considerara que dicha instancia sea lo mismo que "donde".
Para solucionar esto, ingresaremos el siguiente comando, agregandole
una i
al final de la expresion que le asigne a la orden
insensibilidad a mayusculas:
sed -n 's/Donde/donde/gip' hachando.txt
Esto funcionara, pero la mayoria de las veces no querras activar la insensibilidad a mayusculas para todo. En tales casos, podrias usar un grupo de expresion regular que agregue insensibilidad de mayusculas a cadenas especificas.
Por ejemplo, si encierras los caracteres entre corchetes
([]
), seran interpretados como "cualquier caracter de esta
lista de caracteres".
Introduce lo siguiente para incluir "D" y "d" en el grupo a buscar, de forma de asegurar que en la coincidencia entren tanto "Donde" como "donde" (o sea, se haga indistinta si esta con minuscula o mayuscula "donde":
sed -n 's/[Dd]onde/ande/gp' hachando.txt
Tambien podras restringir la sustituciones a determinadas secciones del fichero. Supongamos que el fichero contiene un espaciado erroneo en su primer decima. Podras usar entonces el siguiente comando ya conocido para analizar el primer verso:
sed -n '3,12p' hachando.txt
Busca ahora dos espacios y sustituyelos por uno. Haras esto globalmente de manera que la accion se repita a lo largo de toda la primer linea. Para ser mas claro, la cadena de busqueda es "espacio, espacio asterisco ('' *'')", y la cadena de sustitucion es "espacio". El 3,12 restringe la sustitucion a unicamente las diez lineas del fichero que componen el primer verso.
Todo esto tendra la forma del siguiente comando:
sed -n '3,12 s/ */ /gp' hachando.txt
¡Excelente! Lo que importa aqui es el patron de busqueda. El
asterisco (*
) representa cero o mas del caracter
precedente, que es un espacio. por ello, el patron a buscar hara que sed
analice cadenas de un espacio o mas.
Si sustituyes un espacio simple por cualquier secuencia multiple de espacios, corregiras el fichero a un espaciado regular, con un espacio simple entre cada palabra. En realidad, esto tambien sustituira el uso de espacios simples por otro espacio simple igual, y aunque no es adverso en el resultado, enllentecera la sustitucion.
Si tipeas lo siguiente y reduces el patron de busqueda a un espacio simple, se hara inmediatamente evidente la necesidad de incluir los dos espacios:
sed -n '3,12 s/ */ /gp' hachando.txt
Como el asterisco busca cero o mas del caracter precedente, interpretara cada caracter que no sea un espacio como un "cero espacio" y le aplicara la sustitucion ¡agregando un espacio tras cada caracter!.
Sin embargo, si incluyes dos espacios en la cadena de busqueda, sed debera encontrar al menos un caracter de espacio antes de aplicar la substitucion. Esto asegurara que los caracteres que no son espacios permanezca inalterados.
El texto contiene dos errores. Si ingresas lo siguiente, usando la
-e
(expresion) que ya has utilizado anteriormente, podras
realizar dos o mas sustituciones en simultaneo:
sed -n -e 's/gaucho/hombre/gip' -e 's/obrera/overa/gip' hachando.txt
Podras lograr el mismo resultado utilizando un punto y coma
(;
) para separar ambas expresiones, de la siguiente
manera:
sed -n 's/gaucho/hombre/gip;s/obrera/overa/gip' hachando.txt
Cuando sustituimos "obrera" por "overa" en el siguiente comando, la instancia de "day" en la expresion "well a-day" tambien fue cambiada:
sed -n 's/[Dd]onde/donde/gp' hachando.txt
Para impedir esto, deberas solo intentar sustituir en lineas que coincidan con otro patron. Si modificas el comando para tener una cadena de busqueda al comienzo, solo consideraremos operar en lineas que coincidan con dicha cadena.
Ingresa lo siguiente para hacer que tu patron de busqueda sea la palabra "after".
sed -n '/after/ s/[Dd]ay/week/gp' coleridge.txt
Esto te dara el resultado buscado.
Sustituciones mas complejas
Demosle un descanso a los alambrados y usemos ahora sed para extraer
nombres del fichero /etc/passwd
.
Hay maneras mas cortas de hacerlo (que se expondran luego), pero usemos la forma mas compleja para demostrar otro concepto. Cada item coincidente en un patron de busqueda (llamadas subexpresiones) puede recibir numeraciones (hasta un maximo de nueve items). Podras utilizar dichos numeros en tus comandos de sed para referenciar subexpresiones especificas.
Debes cerrar las subexpresiones entre parentesis precedidas con una
barra invertida (\
) para que esto funcione. De lo contrario
las subexpresiones serian interpretadas como un caracter normal.
Para hacerlo, tipearas lo siguiente:
sed 's/\([^:]*\).*/\1/' /etc/passwd
Desglosemos esto:
sed 's/
: El comando sed y el comienzo de la expresion de sustitucion.\(
: El parentesis abierto [(] precedido por una barra invertida() abre una subexpresion.[^:]*
: La primer subexpresion de la cadena de busqueda contiene un grupo en corchetes. Cuando se lo usa en un grupo, el circunflejo (^) significa “no”. Un grupo significa que cualquier caracter que no sea dos puntos (:) sera aceptado como una coincidencia.\)
: El parentesis cerrado ()
) precedido por una barra invertida (\
)..*
: Esta segunda subexpresion de busqueda significa "cualquier caracter y cualquier cantidad de ellos"./\1
: La porcion de sustitucion de la expresion contiene 1 precedido por una barra invertida (\
). Esto representa el texto que coincide con la primera subexpresion.- ''/'
: La barra (
/) y el apostrofe (
''') terminan el comando de sed.
Lo que todo esto significa es que se buscara cualquier cadena de
caracteres que no contienen un caracter de dos puntos (:
),
las cuales seran la primer instancia de un texto coincidente. Luego, se
buscara cualquier cosa en dicha linea, la cual sera la segunda instancia
del texto coincidente. Se sustituira la linea entera con el texto que
esta indicado en la primer subexpresion.
Cada linea del fichero /etc/passwd
contiene con un
nombre de usuario terminado por un caracter de dos puntos, y luego se
sustituira dicho valor por la linea entera. De esta forma, habremos
aislados los nombre de usuario.
Salida desde
A continuacion, cerraremos la segunda subexpresion entre parentesis
(()
), de modo de poder referenciarla por un numero tambien.
Tambien reemplazaras \1
con \2
. El comando
ahora sustituira la linea entera con todo a partir del primer caracter
de dos puntos (:
), hasta el final de la linea.
Para ello ingresa:
sed 's/\([^:]*\)\(.*\)/\2/'/etc/passwd
Estos cambios pequenos dan por invertir el significado de todo el comando, por lo cual recibiremos todo excepto los nombres de usuarios.
Ahora, echemos un vistazo a una forma rapida y simple de hacer lo mismo:
Nuestro termino de busqueda opera a partir del primer caracter de dos
puntos (:
) hasta el final de la linea. Como nuestra
expresion de sustutucion esta vacia (//), no reemplazara el texto
coincidente con cadena alguna.
De modo que si tipeamos lo siguiente, quitando todo desde el primer
caracter de dos puntos (:
) hasta el final de la linea, se
lograra el efecto anterior de dejar solo los nombres de usuario:
sed 's/:.*//" /etc/passwd
Ahora miremos un ejemplo en el cual referenciamos la primer y segunda coincidencia el mismo comando.
Tenemos un fichero donde comas (,
) separan el nombre y
el apellido. Deseamos listarlos como "apellido, nombre". Podremos usar
cat, como se muestra abajo, para ver que hay en este fichero:
cat nerds.txt
Como casi todos los comandos de sed, este podria en principio parecer inabarcable:
sed 's/^\(.*\),\(.*\)$/\2,\1 /g' participantes.txt
Se trata de un comando de sustitucion como los anteriores, y la cadena de busqueda es bastante simple. Desglosemoslo:
sed 's/
: El comando de sustitucion normal.^
: Como el circunflejo no esta en un grupo ([]), se interpreta como "el comienzo de la linea".\(.*\),
: La primera subexpresinon es cualquier cantidad de cualquier caracter. Esta entre paŕentesis()
, cada una de las cuales esta precedida por una barra invertida\
, de modo que se referenciara por un numero. El comando se interpretara como "buscar desde el comienzo de la linea hasta la primer coma,
cualquier cantidad de cualquier caracter."\(.*\)
: La siguiente subexpresion es -nuevamente- cualquier cantidad de cualquier caracter. Tambien esta entre parentesis,()
ambos de los cuales se hayan precedidos por una barra invertida\
, de modo que podemos referenciar nuevamente el texto coincidente por numero.$/
: El signo peso ($) representa el fin de la linea y permitira a nuestra busqueda continuar hasta el final de la linea. Se usa esto simplemente para presentar aqui el signo peso. Realmente no se necesita, ya que el asterisco (*
) haria llegar hasta el final de la lonea en este escenario. La barra (/
) completa la busqueda de la cadena.\2,\1 /g
': Como cerramos nuestras dos subexpresiones entre parentesis, podemos referir a ambas por sus numeros. Ya que deseamos invertir su orden, las ingresamos como segunda-coincidencia,primera-coincidencia. Los numeros deben ser precedidos por una barra invertida (\
)./g
: Esto activa la operacion global en cada linea.participantes.txt
: El fichero en el que estamos trabajando.
Tambien puedes usar el comando Cortar (c
) para sustituir
lineas entera que coincidan con la cadena de busqueda propuesta.
Tipearemos lo siguiente para buscar una linea con la palabra "neck" en
ella, y la reemplazaremos con una nueva cadena de texto:
sed '/pachamama/c el amor de Pachamama.' hachando.txt
Nuestra nueva linea aparecera en la parte inferior de nuestro extracto. Insertar Lineas y Texto
Tambien podremos insertar nuevas lineas y texto en nuestro fichero.
Para insertar lineas nuevas a continuacion de cualquieras que coincidan,
usaremos el comando Agregar (a
).
He aqui el fichero con el que queremos trabajar:
cat participantes.txt
Hemos numerados las lineas para hacerlo mas simple de entender.
Tipea el siguiente comando para buscar lineas que contengan la palabra "He," e inserta una nueva linea debajo de ellas:
sed '/He/a --> Insertado!' participantes.txt
Tipea lo siguiente e incluye el comando Insertar (i
)
para insertar la nueva linea por encima de aquellas que contienen el
texto coincidente:
sed '/He/i --> Insertado!' participantes.txt
Podemos usar el caracter et o ampersand (&
), que
representa al texto coincidente original, para agregar una cadena de
texto nuevo a la linea coincidente. \1
, \2
y
demas, representan las subexpresiones coincidentes.
Para agregar texto al comienzo de una linea, se utiliza un comando de sustitucion que haga coincidir todo en la linea, combinado con una clausula de reemplazo que combine nuestro nuevo texto con la linea original.
Para hacerlo, introduce lo siguiente
sed 's/.*/--> Insertado &/' participantes.txt
Tipea lo siguiente, incluyendo el comando G, que agregara una linea en blanco entre cada linea:
sed 'G' participantes.txt
Si deseas agregar dos, o tres lineas en blanco, puedes usar
G;G,
G,G,G,
y asi.
Borrar lineas
El comando Borrar (d
) borra las lineas que coincidan con
el patron de busqueda, o aquellas lineas especificadas numeros de lineas
o rangos de lineas.
Por ejemplo, para borrar la tercer linea, tipeariamos lo siguiente:
sed '3d' participantes.txt
Para borrar el rango de linas cuatro a cinco, tipeariamos lo siguiente:
sed '4,5d' participantes.txt
Para borrar las lineas por fuera de un rango dado, usariamos un signo de exclamacion, de la siguiente manera:
sed '6,7!d' participantes.txt
Guardar los Cambios
Hasta ahora, todos los resultados se han presentado en la terminal, pero no los hemos guardado en ningun lado. Para hacerlos permanente, o bien se pueden guardar los cambios en el fichero original, o redirigir la salida a un fichero nuevo.
Sobreescribir el fichero original puede parecer lo obvio a realizar, pero suele requerir cierto cuidado. Si el comando ingresado con sed es incorrecto, podrias realizar cambios al fichero original que fuesen muy dificiles de revertir.
Es una buena politica instruir a sed para que cree una copia de respaldo del fichero original antes de ejecutar cualquier comando.
Puedes usar la opcion En el Lugar (-i
) para indicar a
sed que escriba los cambios al fichero original, pero si le agregas una
extension de archivo, sed procedera a respaldar el fichero original con
dicha extension. Tendra el mismo nombre que el fichero original, pero
con una nueva extension.
Para demostrarlo, busca cualquier linea que contiene la palabra "He"
y borrala. Tambien respaldaremos nuestro fichero original a uno nuevo
aplicandole la extension .bak
.
Para hacerlo asi, ingresa lo siguiente:
sed -i'.bak' '/^.*He.*$/d' participantes.txt
Tipea lo siguiente para asegurarte que tu fichero de respaldo no presenta cambio alguno.
cat participantes.txt.bak
Tambien puedes tipear el siguiente comando para redirigir la salida a un fichero nuevo y lograr un resultado similar:
sed -i'.bak' '/^.*He.*$/d' participantes.txt > nuevos_participantes.txt
Utiliza cat para confirmar que los cambios han sido realizados y escritos en un fichero nuevo, como se indica a continuacion:
cat new_participantes.txt
Habiendo dicho Todo esto
Como has notado, incluso este pequeno apunte de sed es bastante extenso. Existen muchas posibilidades para este comando, y existen muchisimas cosas que puedes realizar con el.
Con suerte, estos conceptos basicos te habran provisto de un cimiento solido sobre el que puedas continuar aprendiendo. ¡Sacia tu sed de sed!