¿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!