GREP y SED

Antecedentes: editor de text ED

ed es un editor de línea. Fue desarrollado en una época en la que las terminales de computadora (teclado y pantalla) no eran lo más corriente. Usualmente se utilizaba una teletipo (una suerte de máquina de escribir: teclado con impresión en papel fanfold),para comunicarse con la computadora y por lo tanto no era posible desplegar datos en una pantalla. Para ahorrar papel, el contenido de un archivo se ingresaba una línea por vez y se empleaban comandos sofisticados para realizar la edición. De esta aplicación surgen maneras muy inteligentes y eficientes de procesar textos. Muchas de las ideas incorporadas a ed para trabajar eficientemente con textos migrarían a programas como vi, grep y sed.

Referencias:

Tutorial muy básico de ed:

Ed tiene 2 modos: comando e inserción

En el modo comando, la sintaxis para ingresar comandos es:

[ dirección[,dirección]]comando[parámetros]

donde lo que aparece entre paréntesis rectos es opcional

Los comandos suelen limitarse a una letra. Las direcciones corresponden a números de línea, los parámetros son datos adicionales que en ocasiones el comando requiere.

ed siempre arranca en modo comando. Para pasar a modo insertar o agregar, deben usarse los comandos i o a. Si se usa el comando i, se inserta texto antes de la línea corriente. Si se usa el comando a, se agrega texto luego de la línea corriente. Para terminar de insertar o agregar texto, se debe insertar una . aislado en una línea. Eso hace que ed pase del modo inserción a modo comando nuevamente.

Sólo es posible ingresar comandos en el modo comando. Mientras que se está en el modo inserción, todo el texto que se inserte pasa a formar parte del archivo que se esté editando.

Mientras se está editando el archivo, el texto existe en un “buffer” (podemos pensarlo como un área de trabajo, en la memoria de la computadora). Para salvar ese texto usamos el comando w:

  • w salva el archivo a disco
  • w nombre salva el contenido del archivo a disco con el nombre que le pongamos

Para salir de ed podemos usar el comando q. Si no queremos salvar el contenido del archivo, pero queremos salir de ed, podemos forzar la salida con Q.

Para distinguir cuando estamos en modo comando o modo inserción, podemos activar un prompt, con el comando P. de esa manera aparece un * (asterisco), que marca que estamos en modo comando.

Direccionamiento:

Cómo elegimos la línea corriente:

  • Si ingresamos un número, esa línea de texto pasa a ser la línea corriente.
  • Ingresando , o p o . se imprime en pantalla el contenido de la línea corriente.
  • Ingresando n, se imprime en pantalla el contenido de la línea corriente, acompañada del correspondiente número de línea.

Por defecto, cualquier comando que se ejecute, tiene efecto únicamente sobre la línea corriente. Para que el comando actúe sobre varias líneas, hay que especificar un rango de direcciones: [dirección[,dirección]]

Rangos de líneas y cambio de línea corriente:

  • 3 nos movemos a la tercer línea (pasa a ser la línea corriente)
  • $ vale por la última línea: la última línea pasa a ser la línea corriente
  • +2 nos movemos 2 líneas hacia adelante
  • -4 nos movemos 4 líneas hacia atrás
  • 3,7 vale por el rango de líneas desde la tercera a la séptima
  • . vale por la línea corriente
  • , vale por todo el rango de líneas. Es equivalente a 1,$
  • .,$ vale por el rango que va de la línea corriente a la última
  • /regex/ nos movemos a la siguiente línea que contenga la expresión regular
  • ?regex? nos movemos a la línea anterior que contenga la expresión regular

Comando habituales:

  • p imprime la línea corriente
  • ,p imprime todas las líneas
  • ,n idem, pero incluyendo el número de línea adelante
  • 3,7n imprimir las líneas de la 3 a la 7 inclusive
  • /regex/ hace corriente la primer línea que contenga la expresión regular
  • g/regex/p imprime las líneas que contengan la expresión regular

Cambiando una línea:

  • 3c pasa a modo inserción y sustituye la línea 3 por el texto ingresado. Se sale del modo inserción con . aislado

Borrando una línea (o rango de líneas):

  • 3d borra (delete) la línea 3
  • 3,7d borra las líneas 3 a 7 inclusive

Moviendo líneas:

  • 3m1 mueve la línea 3 a la primer posición
  • 1m$ mueve la primer línea al último lugar

Sustituyendo texto:

  • s/regex/texto/ sustituye la primer ocurrencia de regex por el correspondiente texto en la línea corriente.

Parámetros adicionales:

  • s/regex/texto/3 sustituye la tercer ocurrencia de regex (si regex está al menos 3 veces presente en la línea
  • s/regex/texto/g sustituye todas las veces que aparezca regex
  • 3s/regex/texto/g sustituye todas las veces que aparezca regex en la línea 3
  • 3,7s/regex/texto/g sustituye todas las veces que aparezca regex entre las líneas 3 y 7
  • ,s/regex/texto/g sustituye regex en todo el archivo

GREP

El comando g/re/p de ed es el origen del nombre del comando grep. Sirve para buscar dentro de uno o más archivos, líneas que contengan una determinada expresión regular.

grep "regex" archivo
grep -e "regex" archivo

grep puede invocarse también como egrep fgrep o rgrep.

  • egrep es equivalente a grep -E: extended regex
  • fgrep es equivalente a grep -F: fixed string (no se interpreta como regex)
  • rgrep es equivalente a grep -r: recursive

Opciones habituales:

  • -i: ignore case
  • -v: invert match (muestra las líneas que NO contienen regex
  • -c: sólo muestra cuantas líneas contienen regex
  • -o: sólo imprime la parte de la línea que cumple con regex
  • -q: quiet, no muestra nada, exit code 0 si encontró algo (se puede usar en setencias if)
  • -H: imprime el nombre del archivo donde encontró regex
  • -n: imprime el número de línea donde encontró regex
  • –color=auto: resalta regex con color
  • -A NUM: imprime la línea que contiene regex y las NUM líneas siguientes (after)
  • -B NUM: idem pero las NUM anteriores (before)
  • -r: busca recursivamente en todos los archivos del path y sus subdirectorios
  • -E: habilita el uso de expresiones regulares extendidas (comparar -F, -P y -G)

Expresiones Regulares

La expresión regular más sencilla es un texto cualquiera.

grep "texto" archivo
grep -i "texto" archivo

Si usamos fgrep o grep -F, entonces se puede emplear cualquier caracter en en el “texto” que se busca. De lo contrario, hay algunos caracteres que tienen significado especial (metacaracteres). Si se desea incluir un metacaracter en la búsqueda, hay que anular su significado, antecediéndolo con \.

Metacaracteres y su uso: 1) conjuntos de caracteres:

  • […] vale por algunos de los caracteres representados por

grep if if.txt

grep "[iI]f" if.txt
grep -i if if.txt
  • [^…] niega los caracteres especificados entre paréntesis rectos (complemento)
  • [.-.] rangos de caracteres. Ejemplo: [a-d6-9] vale por caracteres entre la a y la d o números entre 6 y 9
  • [:alnum:], [:alpha:], etc. (ver manual de grep) corresponden a conjuntos particulares de caracteres

Anclas: comienzo o fin de una línea:

  • ^ y $ corresponden al comienzo y fin de una línea
grep -i if if.txt
grep -i "^if" if.txt

Repetición: metacaracteres que se aplican a una regex que le antecede

  • ? regex aparece cero o a lo sumo una vez
  • * regex aparece cero o más veces
  • + regex aparece 1 o más veces
  • {n} regex aparece exactamente n veces
  • {,m} regex aparece a lo sumo m veces
  • {n,m} regex aparece entre n y m veces

Concatenación: las regex se pueden concatenar y valen por la unión de ambas regex

grep "[iI][fF]" if.txt

Alternación: usando | es posible especificar regex alternativas (equivale al operador lógico OR).

egrep "IF|life" if.txt
egrep "I[fF]|life" if.txt

Precedencia: repetición > concatenación > alternación. La precedencia se puede cambiar usando paréntesis ( ) para agrupar sub-expresiones regulares.

Expresiones regulares básicas vs. extendidas: en modo básico (por defecto), los metacaracteres ?, +, {, |, ( y ) no funcionan, deben usarse precedidos por \. Es preferible siempre usar la grep -E o egrep

Ejemplo más complejo: extraer emails de un mailbox

Si quiero extraer líneas con direcciones de email:

grep -E  "\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}\b" mbox

Si sólo quiero las direcciones de email, uso la opción '-o':

grep -E -o "\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}\b" mbox

Ejemplo: extraer número IP de un mailbox

grep -E -o "([0-9]{1,3}[\.]){3}[0-9]{1,3}" mbox

Ejemplo de PES (Potential Energy Surface):

cálculo de HF: hf.out

Extraigo las distancias:

grep "^ !       R1 " hf.out|grep "DE/DX"|cut -b 22-25 >nada1

Otra forma de hacer esto:

grep '^ !.*DE/DX' hf.out|tr -s " " "\t"|cut -f4

Extraigo las energías:

grep '^ SCF Done:  ' hf.out|cut -b 26-35 >nada2

Junto ambas columnas:

paste nada1 nada2

Finalmente procesamos los datos con pyxplot para generar la curva de PES

SED: Stream Editor

sed es un descendiente directo de ed, sólo que en lugar de trabajar interactivamente con el editor, sed ejecuta uno o más comandos de ed en un stream de bytes que recibe por stdin o desde un archivo, volcando a stdout el resultado. Básicamente todo lo que se puede hacer en ed se puede hacer con sed.

Ejemplos:

sed -nr '/if/p' if.txt
sed -nr '/[iI][fF]/p' if.txt
sed -nr '/^[iI][fF]/p' if.txt
sed -rn 's/If/IF/' if.txt
sed -rn 's/If/IF/p' if.txt
sed -r  's/^I[fF]/SI/' if.txt
sed -r  's/^ *$/    --------/' if.txt

Volvemos al ejemplo del HF: en el ejemplo que vimos usamos cut para extraer las columnas 26 a 35

grep '^ SCF Done: ' hf.out | cut -b 26-35 

pero si sustituimos los espacios en blanco por tabuladores, entonces podemos usar cut haciendo referencia al campo (-f):

grep '^ SCF Done: ' hf.out | sed 's/^ //; s/  */\t/g' | cut -f5

Ojo con los metacaracteres: a veces debo usar regex extendido:

grep '^ SCF Done: ' hf.out | sed -r 's/^ //; s/ +/\t/g' | cut -f5