# rm-rf.es | Administración de sistemas

Bitácora personal de un SysAdmin Gnu/Linux, Windows, BSD...

Ejemplos prácticos de grep y egrep


grep searchGrep es sin duda alguna no de los comandos más utilizados en el día a día por los administradores de sistemas. Muchos no van más allá de sus usos más básicos, que si bien cumplen a la perfección su función son una mínima parte de las posibilidades que nos ofrece. Algunos de estos ejemplos se pueden ejecutar tanto con grep como con egrep (egrep= grep -E), independientemente de que casi siempre se ejecuten con egrep en el post. Espero que os sean de utilidad.

Este es el texto que se va a utilizar para trabajar como ejemplo. Sí, es un fichero de configuración de un Cluster MySQL de prueba, pero es que no tenía otra cosa a mano y no me apetecía preparar un fichero específicamente para esto ;)

[ndb_mgmd]
hostname=192.168.0.10           # Hostname or IP address of management node
datadir=/var/lib/mysql-cluster  # Directory for management node log files

# Options for data node "A":
[ndbd]
                                # (one [ndbd] section per data node)
hostname=192.168.0.30           # Hostname or IP address
datadir=/usr/local/mysql/data   # Directory for this data node's data files

# Options for data node "B":
[ndbd]
hostname=192.168.0.40           # Hostname or IP address
datadir=/usr/local/mysql/data   # Directory for this data node's data files

# SQL node options:
[mysqld]
hostname=192.168.0.20           # Hostname or IP address
                                # (additional mysqld connections can be
                                # specified for this node for various
                                # purposes such as running ndb_restore)

Buscar dos ó n strings distintas dentro de un mismo fichero

La sintaxis es ‘(cadena1|cadena2|cadenaN)’. La salida será todas aquellas líneas que contienen cualquiera de esas strings. Esto nos permite hacer múltiples búsquedas en un único comando:

$ egrep '(192.168.0.30|192.168.0.40)' test
hostname=192.168.0.30           # Hostname or IP address
hostname=192.168.0.40           # Hostname or IP address
$ egrep '(192.168.0.30|192.168.0.40|192.168.0.20)' test
hostname=192.168.0.30           # Hostname or IP address
hostname=192.168.0.40           # Hostname or IP address
hostname=192.168.0.20           # Hostname or IP address

Usar los corchetes para reducir el rango de búsqueda

Podemos hacer uso de los corchetes [] para definir, dentro de una misma string, que una sección contenga únicamente X carácteres, un rango de ellos, etc.

En este caso queremos sacar únicamente los resultados que contengan 192.168.0.30 y 192.168.0.40:

$ egrep 192.168.0.[30,40] test
hostname=192.168.0.30           # Hostname or IP address
hostname=192.168.0.40           # Hostname or IP address

Pero si quisiéramos definir los rangos que queremos mostrar para los dos últimos caracteres, en este caso numéricos (se podría hacer con alfanuméricos) podemos hacerlo separando el valor inicial y el final con “-”. En este ejemplo queremos que nos muestre aquello que cumpla la condición de que el primer número del último bloque tiene que ser 0,1 ó 2:

$ egrep 192.168.0.[0-2]0 test
hostname=192.168.0.10           # Hostname or IP address of management node
hostname=192.168.0.20           # Hostname or IP address

Y en este que el primer número del último bloque sea 1,2 ó 3 y el último entre 0 y 9:

$ egrep 192.168.0.[0-3][0-9] test
hostname=192.168.0.10           # Hostname or IP address of management node
hostname=192.168.0.30           # Hostname or IP address
hostname=192.168.0.20           # Hostname or IP address

Un ejemplo práctico con caracteres alfanuméricos sería usar por ejemplo [a-c]test, que buscaría atest, btest y ctest ó por ejemplo [ar4d]test que buscaría atest, rtest, 4test y dtest.

Uso de clases predefinidas

Existen clases predefinidas que nos pueden llegar a ahorrar mucho trabajo. Son las siguientes:

[:alnum:], [:alpha:], [:cntrl:], [:digit:], [:graph:], [:lower:], [:print:], [:punct:], [:space:], [:upper:], [:xdigit:]

Lo que hace cada una de ellas está claro por su nombre, no obstante vamos a ver un ejemplo. Podemos buscar toda línea que contenga un carácter en mayúsculas:

$ egrep [[:upper:]] test
hostname=192.168.0.10           # Hostname or IP address of management node
datadir=/var/lib/mysql-cluster  # Directory for management node log files
...
...

Todo lo que comienza o termina por…

Esto es sencillo, las líneas comienzan por el carácter ^ y terminan con $. Por lo que:

Buscar todo lo que comience por “hostname”:

$ egrep ^hostname test
hostname=192.168.0.10           # Hostname or IP address of management node
hostname=192.168.0.30           # Hostname or IP address
hostname=192.168.0.40           # Hostname or IP address
hostname=192.168.0.20           # Hostname or IP address

O todo lo que termine por “node”:

$ egrep node$ test
hostname=192.168.0.10           # Hostname or IP address of management node

Más expresiones regulares

Ya hicimos un artículo sobre ello hace tiempo, podéis revisarlo aquí: unix: Expresiones regulares, ahí también encontraréis unos cuantos ejemplos de grep. Son muy útiles los operadores de repetición:

       ?      El carácter que precede es opcional y coincide al menos una vez.
       *      El carácter que precede coincidirá 0 o más veces.
       +      El carácter que precede coincidirá 1 o más veces.
       {n}    El carácter que precede coincidirá exactamente n veces.
       {n,}   El carácter que precede coincidirá n o más veces.
       {,m}   El carácter que precede coincidirá como máximo m veces.
       {n,m}  El carácter que precede coincidirá entre n y m veces.

Podemos entonces buscar todas las líneas del fichero que contienen una IP. Usamos primero los corchetes para definir que puede haber números del 0 al 9, con las llaves decimos que habrá 1 o 3 números en cada bloque y así cuatro veces (nota: hay que escapar las llaves):

$ grep '[0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]\{1,3\}' test
hostname=192.168.0.10           # Hostname or IP address of management node
hostname=192.168.0.30           # Hostname or IP address
hostname=192.168.0.40           # Hostname or IP address
hostname=192.168.0.20           # Hostname or IP address

Buscar palabras completas, contar resultados

Para indicar a grep que queremos que la búsqueda se centre en palabras y no en caracteres sueltos especificamos el parámetro “-w”. Podemos ver la diferencia buscando “data” en el ejemplo, contamos los resultados satisfactorios con el parámetro “-c”:

$ grep -cw data test
5
$ grep -c data test
6

Excluir resultados, sensibilidad a mayúsculas

Algo básico a la hora de usar grep es conocer los parámetros “-v” que excluye la cadena indicada en el resultado y “-i” que especifica que no se tendrá en cuenta si el resultado es mayúscula o minúscula.

Eliminamos las líneas que tienen comodines:

$ grep -v "#" test
[ndb_mgmd]

[ndbd]

[ndbd]

[mysqld]

Uso de pipes

Todos usamos pipes (|) para acotar resultados. Hacemos un primer grep, con el resultado de ese hacemos otro y así sucesivamente:

$ grep -iw hostname test | grep 192.168.0.[0-9]0 | grep -v 192.168.0.40 | grep -vw node
hostname=192.168.0.30           # Hostname or IP address
hostname=192.168.0.20           # Hostname or IP address

Listar los archivos que contienen la cadena

Con el parámetro -l podemos ejecutar grep contra varios archivos (y de forma recursiva en múltiples directorios con -R) y recibiremos el listado de archivos con coincidencias sin mostrar las líneas que contienen la cadena:

$ grep -lR SQL *
config.ini
header.xcf
iptables.sh
test
dir1/test.sql
dir2/prueba.txt
...

Número de línea en la que se encuentran los resultados

Para saber el número de línea en la que se encuentra el resultado utilizaremos el parámetro -n:

$ grep -n hostname test
2:hostname=192.168.0.10           # Hostname or IP address of management node
8:hostname=192.168.0.30           # Hostname or IP address
...

Cuantas veces se encuentran resultados

Si “-n” nos monstraba el nº de línea en el que estaba la coincidencia, “-c” nos dice cuantas veces está la búsqueda en el fichero:

$ grep -c hostname test
4

Bueno, de momento esto es todo. Por supuesto hay muchas más opciones y explotar la potencia de grep llevaría mucho tiempo así que a partir de aquí investigáis vosotros ;)

AWK: imprimir a partir de una expresión regular hasta el final del fichero


En el caso de necesitar recoger de un fichero a partir de una determinada cadena de texto hasta el final del mismo, podemos utilizar AWK del siguiente modo:

awk '/regex/,0'

ó

 awk '/regex/,EOF'

Voy a mostrar un ejemplo, tenemos un fichero de texto que contiene lo siguiente:

vim test
esto es
una prueba
test
probando

Pongamos el caso de que necesitamos sacar el contenido del fichero “test” a partir de la línea que tenga el texto prueba:

#] awk '/prueba/,0' test
una prueba
test
probando

En este caso es un ejemplo muy sencillo, para ficheros de log o similares podéis hacer uso de la potencia de las expresiones regulares para sacar la cadena de texto a partir de la cual imprimir el contenido del fichero.

Os recomiendo también leer este otro artículo de iniciación a AWK.

Vim: Manual de expresiones regulares


Os dejo el enlace a un buen manual de expresiones regulares para el potente VIM:

- Enlace manual de expresiones regulares de Vim

En el manual se tratarán entre otras cosas comandos de sustitución, patrones, cuantificadores, etc.

Unix: Expresiones regulares


En este artículo pretendo entrar en el mundo de las expresiones regulares en Unix sin complicarnos demasiado, me gustó la definición de expresiones regulares que hacen en desarrollo web:

- Las expresiones regulares son una serie de carácteres que forman un patrón, normalmente representativo de otro grupo de carácteres mayor, de tal forma que podemos comparar el patrón con otro conjunto de carácteres para ver las coincidencias.

(^) La expresión encaja o coincide al principio de la línea, por ejemplo ^A
(?) La expresión encaja o coincide al final de la línea, por ejemplo A?
(\) Escapa el carácter siguiente a la contrabarra, haciendo que deje de ser un carácter especial, ejemplo in \*
([]) La expresión encaja o coincide con alguno de los caracteres incluidos entre los corchetes, también se puden especificar rangos, por ejemplo [aeiou] o [0-9].
[^ ] La expresión encaja o coincide con cualquier carácter excepto los que se encuentran entre corchetes, ejemplo, todos los carácteres excepto del 0 al 9: [^0-9]
(.) La expresión encaja respecto a un único carácter, de cualquier valor, excepto al final de la línea.
(*) La expresión coincide con 0 o más carácteres de la expresión que le precede.
\{x,y\} La expresión encaja o coincide desde X hasta Y respecto a lo que la ocurrencia que le precede.
\{x\} La expresión encaja exactamente X ocurrencias de lo que le precede.
\{x,\} La expresión encaja exactamente X o más ocurrencias de lo que le precede.

Algunas de estas expresiones regulares pueden resultar confusas, lo mejor es poner unos cuantos ejemplos para aclarar algunas de ellas:

$ # Listar todas las líneas que comienzan por "From:" de nuestros emails:
$ grep '^From: ' /usr/mail/$USER
$ # Buscar líneas con al menos una letra de la a a la z (incluidas mayúsculas) de un fichero concreto.
$ grep '[a-zA-Z]'  search-file.txt
$ # Lista cualquier carácter que no sea ni letra ni número de un fichero concreto.
$ grep '[^a-zA-Z0-9] search-file.txt
$ # Buscar teléfonos del tipo 999-9999  de un fichero concreto.
$ grep '[0-9]\{3\}-[0-9]\{4\}' search-file.txt
$ # Buscar líneas de un fichero con solamente 1 carácter:
$ grep '^.$' search-file.txt
$ # Buscar líneas que comienzan con el carácter "."
$ grep '^\.' search-file.txt
$ # Buscar líneas que comienzan por un "." y dos letras en minúscula:
$ grep '^\.[a-z][a-z]' search-file.txt

Y podríamos estar poniendo ejemplos hasta el infinito, este artículo está basado en uno de la genial web de IBM, además podéis encontrar muchísima información sobre expresiones regulares en estos enlaces:

- Curso básico de Unix: Expresiones regulares
- Más expresiones regulares e información por parte de IBM.