Editando texto en BASH con el comando sed

El resultado de escribir man sed en la terminal de Linux

Esta tarde he recibido un email de OVH diciendo que me quedaba una semana para que mi alojamiento compartido se terminara. Como a pesar de que me queje (porque un fin de semana sin server es apocalíptico), no me han dado muchos problemas este año, he decidido renovar con ellos, cambiando mi antiguo “plan90″ (ahora ya no existe con ese nombre) por otro “profesional” que me venía mucho mejor (necesitaba una buena BD MySQL, bastante espacio y mucho tráfico mensual) y portando todo al nuevo servidor.

El caso es que la base de datos de nuestra aplicación “estrella” de Facebook es bastante inmanejable para un PHPMyAdmin, son 117 MB de backup. En una supertabla con millones de entradas teníamos un par de campos que no se usaban, y que ocupaban una burrada, no por su longitud sino por el número de instancias. Después de tratar unas cuantas veces de borrarlos por SSH, usando el comando “mysql”, y desde la interfaz web sin resultados me he puesto a buscar la mejor manera de modificar el script de backup.

Resulta que teníamos varios millones de inserciones del estilo “INSERT INTO usuario VALUES (’121900349302′, 13, 44, 27);”, donde los dos últimos número sobraban porque eran los valores de los campos que no quería usar más. El primero de los dos podía recibir un NULL, y el segundo valores negativos. Y aquí entra “sed“:

El comando sigue la estructura “sed [opciones] instrucciones [fichero]“. Si no se especifica un fichero lee de la entrada estándar (así que se le pueden pasar valores con un pipeline | ). Las instrucciones deben ir entre comillas simples, para que el comando no trate de explotar símbolos como * o $. Si son de sustitución deben empezar por s y seguir este formato: ‘s/VIEJA/NUEVA/’
Por ejemplo:

    echo "Vivo en: C/ Fuencarral" | sed 's/C\//la calle/'

Daría como resultado:

    Vivo en: la calle Fuencarral

Como podéis ver he escapado la barra / con otra barra inversa delante \. VIEJA acepta patrones (otro día hablare de ellos), y el “juego de metacaracteres extendido” (para poder usar patrones con: *, ?, +, [ - ], {,}, etc) si usamos la opción -E, por ejemplo:

    echo "Vivo en: C/ Fuencarral" | sed -E 's/[a-zA-Z]{5,10}/Gran Vía/g'

Tiene por salida:

    Vivo en: C/ Gran Vía

En el ejemplo le he dicho que sustituya todas las cadenas formadas por letras mayúsculas o minúsculas de entre 5 y 10 caracteres por “Gran Vía”, por lo que ha sustituido “Fuencarral”. He añadido además una g después de la última barra, que significa que no se pare en la primera que encuentre, que sustituya hasta el final.

Es posible también aplicar varias instrucciones seguidas separadas por punto y coma, de esta forma:

    sed 's/a/A; s/b/B' fichero_a_modificar.txt

o separándolas con la opción -e:

    sed -e 's/a/A' -e 's/b/B' fichero_a_modificar.txt

También es posible que decirle que coja las instrucciones de un fichero con la opción -f seguida del nombre:

    sed -f reglas.sed fichero_a_modificar.txt

Y evitar que muestre el resultado por pantalla con -n:

    sed  -n 's/b/B' fichero_a_modificar.txt

Mi problema con las tablas se ha solucionado con:

    sed -E 's/, (NULL|[0-9]+), -?[0-9]+\)/)/g' backup.sql > backup_limpio.sql

Que sobre un fichero que contenga:

    INSERT INTO usuario VALUES ('121900349302', 13, NULL, -27);
    INSERT INTO usuario VALUES ('121900349303', 13, 43, 37);
    INSERT INTO usuario VALUES ('121900349304', 13, NULL, -18);
    INSERT INTO usuario VALUES ('121900349305', 13, 45, 9);

Obtiene:

    INSERT INTO usuario VALUES ('121900349302', 13);
    INSERT INTO usuario VALUES ('121900349303', 13);
    INSERT INTO usuario VALUES ('121900349304', 13);
    INSERT INTO usuario VALUES ('121900349305', 13);

¡Si alguien encuentra algo interesante sobre “sed” o patrones que lo comente!

Esta entrada fue publicada en Lenguaje, Linux y etiquetada , , , , , , , , , , , , , . Guarda el enlace permanente.

4 respuestas a Editando texto en BASH con el comando sed

  1. Pingback: URLs bonitas modificando .htaccess | TecnoCaos

  2. inukaze dice:

    tengo una duda muy simple , si quiero hacer

    echo $HOME
    /home/user_name

    como hago que sed borre “/home/” ??? me gustaria que el resultado fuera
    asignado a una varibale.

  3. Martín Chamarro dice:

    prueba con “echo $USER”

  4. blissett dice:

    Tengo un archivo con datos agrupados en una cantidad variable de lineas consecutivas y separados entre ellos por uno o dos espacios en blanco del tipo:

    gonzalez
    antonio gonzalez izquierdo
    c/ buendia 12
    madrid

    rodriguez
    jose rodriguez gomez
    informatico de cuidado
    un buen elemento
    c/ norte 23
    segovia

    gutierrez
    cuesta de arriba 27
    ciudad real

    Quisiera enviar cada grupo de datos de este documento a un archivo. En el ejemplo que he puesto tendría que dar como resultado 3 ficheros: uno con los datos de gonzalez, otro con los datos de rodriguez y el último con los de gutierrez.
    Estoy batallando con sed y awk pero mis conocimientos de bash scripting son más bien escasos. ¿Puedes echarme una mano?

Deja un comentario

Tu dirección de correo electrónico no será publicada.

Puedes usar las siguientes etiquetas y atributos HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">