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

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

Debug de procesos en Solaris con Truss

Podríamos decir que truss es el equivalente en Solaris a strace de Linux, si bien existen también otras alternativas como dtrace que también cumplen perfectamente esta función.

Truss es una utilidad que permite ejecutar el comando especificado o un proceso concreto y muestra durante su ejecución las llamadas al sistema que ejecuta y las señales que recibe. Esto permite ver todo el proceso de ejecución y hacer debug de cualquier error o fault que ocurre.

Vamos a ver unos ejemplos. Lo más básico sería ejecutar truss seguido del comando del que queremos ver su debug, en el siguiente ejemplo simplemente ejecutamos el comando df:

# truss df
execve("/usr/gnu/bin/df", 0x08047E6C, 0x08047E74)  argc = 1
sysinfo(SI_MACHINE, "i86pc", 257)               = 6
mmap(0x00000000, 32, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON, -1, 0) = 0xD1BB0000
mmap(0x00000000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0) = 0xD1BA0000
mmap(0x00000000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0) = 0xD1B90000
mmap(0x00000000, 4096, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON, -1, 0) = 0xD1B80000
memcntl(0xD1BB8000, 32064, MC_ADVISE, MADV_WILLNEED, 0, 0) = 0
memcntl(0x08050000, 15312, MC_ADVISE, MADV_WILLNEED, 0, 0) = 0
resolvepath("/usr/lib/ld.so.1", "/lib/ld.so.1", 1023) = 12
resolvepath("/usr/gnu/bin/df", "/usr/gnu/bin/df", 1023) = 15
sysconfig(_CONFIG_PAGESIZE)                     = 4096
stat64("/usr/gnu/bin/df", 0x08047AB0)           = 0
open("/var/ld/ld.config", O_RDONLY)             Err#2 ENOENT
stat64("/lib/libc.so.1", 0x08047260)            = 0
...
...
...

No muestro todo el output debido a su longitud, podemos volcarlo a un fichero para analizarlo mejor en lugar de la salida stderr:

# truss -o salida.out df

En el siguiente ejemplo hacemos debug de un proceso que ya se encuentra en ejecución:

# truss -rall -wall -f -p <PID>

“-rall” implica ver todos los datos de lectura y “-wall” todos los de escritura, con “-f” vemos los procesos fordked y “-p” especifica el PID.

Podéis ver más ejemplos e información del comando en la página man correspondiente. A continuación podéis ver un par:

$ man truss

Trazar las llamadas de sistema open, close, read y write únicamente:

# truss -t open,close,read,write find . -print >salida.out

Trazar todas las llamads a funciones a nivel de usuario desde y hacia cualquier sitio:

# truss -u a.out -u ld:: -u :: <comando>

star: empaquetar preservando ACL y atributos extendidos (SElinux)

El comando star permite comprimir y descomprimir y/o empaquetar múltiples ficheros con la posibilidad de mantener tanto los atributos extendidos de SElinux como las ACL que puedan tener configurados los ficheros/directorios.

# yum info star.i686
...
Name        : star
Arch        : i686
Summary     : An archiving tool with ACL support
URL         : http://cdrecord.berlios.de/old/private/star.html
Description : Star saves many files together into a single tape or disk archive,
            : and can restore individual files from the archive. Star supports ACL.
...

No es un comando que venga por defecto, así que lo instalamos con yum:

# yum info star.i686

La utilización no es igual que el comando tar, es más, es bastante compleja. Sólo hay que mirar la página man, que es muy extensa para darse cuenta de las posibilidades que ofrece. Nosotros vamos a ver unos ejemplos básicos, luego cada uno que haga las pruebas e investigaciones oportunas.

Mantener los atributos extendidos de SElinux y ACL

Para incluir en el fichero empaquetado/comprimido cualquier atributo extendido de ficheros así como las ACL utilizamos los parámetros -xattr (podemos recordarlo por extended attributes) y -H=exustar. Para preservar las ACL es más sencillo (-acl). Vamos a empaquetar los siguientes ficheros, que tienen atributos extendidos de SElinux:

# ls -Z
-rw-------. root root system_u:object_r:etc_t:s0       ftpusers
-rw-------. root root system_u:object_r:etc_t:s0       user_list
-rw-------. root root system_u:object_r:etc_t:s0       vsftpd.conf
-rwxr--r--. root root system_u:object_r:etc_t:s0       vsftpd_conf_migrate.sh

Probamos también a meter una ACL para ver que la mantiene. Podéis revisar esta entrada sobre ACLs: ACL (Access Control List) en sistemas de ficheros GNU/Linux.

# setfacl -m u:prueba:r-- ftpusers
# getfacl ftpusers
# file: ftpusers
# owner: root
# group: root
user::rw-
user:prueba:r--
group::---
mask::r--
other::---

El comando sería el siguiente. Podéis ver que especificamos la creación de un nuevo fichero (-c) y su nombre (-f=):

# star -xattr -H=exustar -acl -c -f=ejemplo.star *
star: 1 blocks + 0 bytes (total of 10240 bytes = 10.00k).

Desempaquetar el fichero es más sencillo, usamos el parámetro -x (desempaquetar) y especificamos el fichero de la misma forma que antes (-f=nombre). Si queremos mantener las ACL hay que especificarlo de la misma forma que al empaquetar (-acl):

# star -x -acl -f=ejemplo.star
star: 1 blocks + 0 bytes (total of 10240 bytes = 10.00k).

Y revisamos los atributos de SElinux:

# ls -Z
-rw-r-----. root root system_u:object_r:etc_t:s0       ftpusers
-rw-r--r--. root root unconfined_u:object_r:etc_t:s0   test.star
-rw-------. root root system_u:object_r:etc_t:s0       user_list
-rw-------. root root system_u:object_r:etc_t:s0       vsftpd.conf
-rwxr--r--. root root system_u:object_r:etc_t:s0       vsftpd_conf_migrate.sh

Y las ACL:

# getfacl ftpusers
# file: ftpusers
# owner: root
# group: root
user::rw-
user:prueba:r--
group::---
mask::r--
other::---

De esta forma veréis que únicamente hemos empaquetado, no hemos comprimido. Con el parámetro -compress-program= podemos especificar el compresor a utilizar (gzip, bzip2,etc)

# star -xattr -H=exustar -compress-program=gzip -c -f=test.star *
star: 2 blocks + 0 bytes (total of 20480 bytes = 20.00k).

Vemos que efectivamente el peso es mínimo:

-rw-r--r--. 1 root root 2997 feb 27 20:41 test.star

Para descomprimir es igual, el propio comando detectará la compresión y actuará en consecuencia:

# star -x -acl -f=test.star
star: WARNING: Archive is 'gzip' compressed, trying to use the -z option.

Subnetting y cálculo de netmask desde línea de comandos

Si se os da tan mal como a mí el cálculo de subredes o simplemente queréis una herramienta rápida para hacerlo aquí tenéis ipcalc o sipcalc. A través de este comando podremos calcular subredes directamente desde la línea de comandos. La principal diferencia entre ambos es que sipcalc acepta IPV6 y permite subnetting.

Para instalarlos utilizamos los gestores de paquetes correspondientes, apt, yum…

$ sudo apt-get install ipcalc
$ sudo yum install ipcalc

Nota: en RHEL y CentOS hay que activar el repositorio rpmforge.

La utilización es bien sencilla, podemos pasar como parámetro la subred en formato CIDR y nos devuelve toda la información de la misma, máscara, red, nº de hosts, etc:

$ ipcalc 10.0.0.0/24
Address:   10.0.0.0             00001010.00000000.00000000. 00000000
Netmask:   255.255.255.0 = 24   11111111.11111111.11111111. 00000000
Wildcard:  0.0.0.255            00000000.00000000.00000000. 11111111
=>
Network:   10.0.0.0/24          00001010.00000000.00000000. 00000000
HostMin:   10.0.0.1             00001010.00000000.00000000. 00000001
HostMax:   10.0.0.254           00001010.00000000.00000000. 11111110
Broadcast: 10.0.0.255           00001010.00000000.00000000. 11111111
Hosts/Net: 254                   Class A, Private Internet

IPV6 con sipcalc:

$ sipcalc 2001:DB8::/24
-[ipv6 : 2001:DB8::/24] - 0

[IPV6 INFO]
Expanded Address	- 2001:0db8:0000:0000:0000:0000:0000:0000
Compressed address	- 2001:db8::
Subnet prefix (masked)	- 2001:d00:0:0:0:0:0:0/24
Address ID (masked)	- 0:b8:0:0:0:0:0:0/24
Prefix address		- ffff:ff00:0:0:0:0:0:0
Prefix length		- 24
Address type		- Aggregatable Global Unicast Addresses
Network range		- 2001:0d00:0000:0000:0000:0000:0000:0000 -
			  2001:0dff:ffff:ffff:ffff:ffff:ffff:ffff

Otra característica interesante es la posibilidad de sipcalc de especificar una red y hacer subneting con ella, si por ejemplo queremos dividir un /21 en redes /22 o /24:

$ sipcalc -s24 10.0.0.0/21
-[ipv4 : 10.0.0.0/21] - 0

[Split network]
Network			- 10.0.0.0        - 10.0.0.255
Network			- 10.0.1.0        - 10.0.1.255
Network			- 10.0.2.0        - 10.0.2.255
Network			- 10.0.3.0        - 10.0.3.255
Network			- 10.0.4.0        - 10.0.4.255
Network			- 10.0.5.0        - 10.0.5.255
Network			- 10.0.6.0        - 10.0.6.255
Network			- 10.0.7.0        - 10.0.7.255

-
$ sipcalc -s22 10.0.0.0/21
-[ipv4 : 10.0.0.0/21] - 0

[Split network]
Network			- 10.0.0.0        - 10.0.3.255
Network			- 10.0.4.0        - 10.0.7.255

Si revisáis la ayuda del comando encontraréis otras posibilidades de interés.

Consultar IMAP o POP desde shell con mutt

En su día ya vimos como acceder vía telnet a un servidor POP. Lo mismo es posible para IMAP, aunque el comando mutt nos facilita las cosas si no queremos saber utilizar con profundidad el protocolo por telnet.

Podéis encontrar mutt a través de cualquier gestor de paquetes (apt,yum…), así que su instalación no tiene ningún misterio. Para acceder a una cuenta IMAP ejecutaremos el siguiente comando:

# mutt -f imap://test@test.com@mail.test.com

En este caso accedemos a la cuenta test@test.com en el servidor IMAP mail.test.com. Para POP lo mismo pero cambiando el protocolo:

# mutt -f pop://test@test.com@mail.test.com

Una vez dentro es tan simple como usar los cursores y ENTER para movernos por los mensajes y/o seguir las indicaciones del menú:

q:Salir  d:Sup.  u:Recuperar  s:Guardar  m:Nuevo  r:Responder  g:Grupo  ?:Ayuda
mutt

Ver el contenido de btmp y wtmp con utmpdump

El comando utmpdump permite visualizar el contenido de los ficheros btmp y wtmp. Ambos ficheros tienen formato binario y almacenan logs de:

  • btmp: log que almacena un registro de los accesos fallidos al sistema
  • wtmp: log que almacena un registro de los accesos al sistema

Los ficheros son ilegibles de forma directa debido a que están almacenados en binario:

# file wtmp
wtmp: data

Para ello tenemos el comando utmpdump. Simplemente pasamos el fichero de log como parámetro y podremos visualizar su contenido:

# utmpdump /var/log/btmp 
Utmp dump of /var/log/btmp
[6] [01585] [    ] [alex    ] [ssh:notty   ] [192.168.1.128       ] [192.168.1.128  ] [Mon Jan 16 21:45:56 2012 CET]
[6] [01585] [    ] [alex    ] [ssh:notty   ] [192.168.1.128       ] [192.168.1.128  ] [Mon Jan 16 21:45:59 2012 CET]
[6] [02927] [    ] [foo  ] [ssh:notty   ] [192.168.1.128       ] [0.0.0.0        ] [Fri Jan 27 21:52:13 2012 CET]
[6] [03787] [    ] [root    ] [ssh:notty   ] [192.168.1.128       ] [192.168.1.128  ] [Fri Jan 27 22:01:35 2012 CET]
[6] [03787] [    ] [root    ] [ssh:notty   ] [192.168.1.128       ] [192.168.1.128  ] [Fri Jan 27 22:01:51 2012 CET]
# utmpdump /var/log/wtmp | head
Utmp dump of /var/log/wtmp
[2] [00000] [~~  ] [reboot  ] [~           ] [2.6.32-220.el6.i686 ] [0.0.0.0        ] [Wed Dec 28 21:01:04 2011 CET]
[1] [00051] [~~  ] [runlevel] [~           ] [2.6.32-220.el6.i686 ] [0.0.0.0        ] [Wed Dec 28 21:01:04 2011 CET]
[6] [01052] [1   ] [LOGIN   ] [tty1        ] [                    ] [0.0.0.0        ] [Wed Dec 28 21:01:25 2011 CET]
[6] [01054] [2   ] [LOGIN   ] [tty2        ] [                    ] [0.0.0.0        ] [Wed Dec 28 21:01:25 2011 CET]
[6] [01056] [3   ] [LOGIN   ] [tty3        ] [                    ] [0.0.0.0        ] [Wed Dec 28 21:01:25 2011 CET]
[6] [01060] [4   ] [LOGIN   ] [tty4        ] [                    ] [0.0.0.0        ] [Wed Dec 28 21:01:25 2011 CET]
[6] [01062] [5   ] [LOGIN   ] [tty5        ] [                    ] [0.0.0.0        ] [Wed Dec 28 21:01:25 2011 CET]
[6] [01064] [6   ] [LOGIN   ] [tty6        ] [                    ] [0.0.0.0        ] [Wed Dec 28 21:01:25 2011 CET]
[7] [01052] [1   ] [root    ] [tty1        ] [                    ] [0.0.0.0        ] [Wed Dec 28 21:01:31 2011 CET]
[7] [03021] [ts/0] [root    ] [pts/0       ] [192.168.1.128       ] [192.168.1.128  ] [Wed Dec 28 21:10:28 2011 CET]

utmpdump también nos permite visualizar el contenido del log a tiempo real (como un tail -f):

# utmpdump -f /var/log/wtmp 
Utmp dump of /var/log/wtmp
[8] [01555] [6   ] [        ] [tty6        ] [                    ] [0.0.0.0        ] [Fri Jan 27 22:20:43 2012 CET]
[2] [00000] [~~  ] [reboot  ] [~           ] [2.6.32-220.el6.i686 ] [0.0.0.0        ] [Sat Jan 28 10:34:12 2012 CET]
[1] [00051] [~~  ] [runlevel] [~           ] [2.6.32-220.el6.i686 ] [0.0.0.0        ] [Sat Jan 28 10:34:12 2012 CET]
...
...

Comando sar: controlar la actividad de CPU (I)

En Linux y Unix existen otros comandos además de top para monitorizar de un modo eficiente la utilización de las CPU en el sistema. Hoy vamos a ver unos cuantos ejemplos del comando sar. En este primer artículo nos centramos en la CPU, veremos en sucesivos que podemos ir más allá (disco, red, procesos, carga, etc).

Para poder comenzar a utilizar sar, debemos tener en cuenta que es necesario por un lado tener corriendo el proceso sysstat:

# /etc/init.d/sysstat start
# chkconfig sysstat on

Además, hay que configurar sysstat para permitir la recolección de datos. Estos datos estadísticos se almacenan en /var/log/sysstat/sa*. Para ello es necesario tener estos parámetros en el fichero de configuración /etc/sysconfig/sysstat. Lo hacemos antes de reiniciar sysstat:

ENABLED="false"
SA1_OPTIONS="-S DISK"

Normalmente, si se instala el paquete por yum o apt, automáticamente se configuran los cron necesarios para recolectar la información que utiliza sar:

$ more /etc/cron.d/sysstat
# Global variables:
#
#  our configuration file
DEFAULT=/etc/default/sysstat
#  default setting, overriden in the above file
ENABLED=false
SA1_OPTIONS=""

# Activity reports every 10 minutes everyday
5-55/10 * * * * root [ -x /usr/lib/sysstat/sa1 ] && { [ -r "$DEFAULT" ] && . "$DEFAULT" ; [ "$ENABLED" = "true" ] && exec /usr/lib/sysstat/
sa1 $SA1_OPTIONS 1 1 ; }

# Additional run at 23:59 to rotate the statistics file
59 23 * * * root [ -x /usr/lib/sysstat/sa1 ] && { [ -r "$DEFAULT" ] && . "$DEFAULT" ; [ "$ENABLED" = "true" ] && exec /usr/lib/sysstat/sa1
$SA1_OPTIONS 60 2 ; }

Vamos a ver unos cuantos ejemplos, os recomiendo como siempre revisar la página man porque es un comando con mucho potencial y opciones.

Mostrar el uso de CPU en un intervalo de tiempo determinado

El siguiente ejemplo muestra el uso de cpu cada 2 segundo:

# sar -u 2
Linux 2.6.28-17-generic (sistemas) 	22/01/12 	_i686_	(2 CPU)

19:14:05        CPU     %user     %nice   %system   %iowait    %steal     %idle
19:14:07        all     12,08      0,00      3,86      0,72      0,00     83,33
19:14:09        all      8,52      0,00      2,19      0,00      0,00     89,29
19:14:11        all      8,39      0,00      2,88      0,72      0,00     88,01
...
...
...

EL siguiente ejemplo hace lo mismo a excepción de que se ejecutará únicamente 4 veces. El anterior por contra se ejecutaba continuamente hasta que lo cancelaramos. Lo interesante de este ejemplo es que al final nos muestra una media de utilización durante el tiempo que se ha ejecutado:

# sar -u 2 4
Linux 2.6.28-17-generic (sistemas) 	22/01/12 	_i686_	(2 CPU)

19:16:39        CPU     %user     %nice   %system   %iowait    %steal     %idle
19:16:41        all     35,47      0,00      6,40      3,20      0,00     54,93
19:16:43        all     26,57      0,00     10,78      0,00      0,00     62,66
19:16:45        all     13,87      0,00      3,65      1,46      0,00     81,02
19:16:47        all     19,07      0,00      5,13      0,00      0,00     75,79
Media: all 23,69 0,00 6,46 1,17 0,00 68,68

Para los que no conozcáis el significado de cada columna:

  • CPU: CPUs que se stán monitorizando, en los casos anteriores todas las del equipo.
  • %user:  Porcentaje de tiempo de CPU utilizada por aplicaciones/procesos a nivel de usuario.
  • %nice:  Porcentaje de tiempo de CPU utilizada por aplicaciones/procesos con prioridad nice asignada.
  • %system:  Porcentaje de tiempo de CPU utilizada por aplicaciones/procesos a nivel de sistema/kernel.
  • %iowait:  Porcentaje de tiempo de CPU en espera a que terminen operaciones de I/O.
  • %steal:  Porcentaje de tiempo utilizado por las CPU en involuntary wait mientras el hypervisor servía a otro procesador virtual
  • %idle: Porcentaje de tiempo de CPU en espera y sin operaciones de I/O pendientes.

Por supuesto se pueden añadir más columnas, revisad las páginas man:

# sar -u ALL 2 4
Linux 2.6.28-17-generic (sistemas) 	22/01/12 	_i686_	(2 CPU)

19:28:16        CPU      %usr     %nice      %sys   %iowait    %steal      %irq     %soft    %guest     %idle
19:28:18        all      9,18      0,00      2,90      0,97      0,00      0,00      0,00      0,00     86,96
19:28:20        all      7,95      0,00      4,34      0,00      0,00      0,00      0,00      0,00     87,71
19:28:22        all     14,46      0,00      5,15      1,47      0,00      0,25      0,00      0,00     78,68
19:28:24        all     17,89      0,00      5,39      0,00      0,00      0,00      0,00      0,00     76,72
Media:          all     12,34      0,00      4,44      0,61      0,00      0,06      0,00      0,00     82,55

Si ejecutaramos sar -u sin parámeteros nos mostaría la CPU utilizada durante el día, sacándola de los ficheros de estadísticas.

Monitorizar por CPU

En lugar de mostrar el estado de todas las CPU a la vez podemos seleccionar la que queramos o incluso mostrar una fila para cada CPU de forma independiente. En el siguiente ejemplo monitorizamos la CPU 1 durante 2 segundos y cuatro ejecuciones:

$ sar -P 1 2 4
Linux 2.6.28-17-generic (sistemas) 	22/01/12 	_i686_	(2 CPU)

19:29:17        CPU     %user     %nice   %system   %iowait    %steal     %idle
19:29:19          1     22,49      0,00      2,39      0,48      0,00     74,64
19:29:21          1      3,33      0,00      2,86      0,00      0,00     93,81
19:29:23          1     17,92      0,00      5,19      0,00      0,00     76,89
19:29:25          1     26,09      0,00     14,01      0,00      0,00     59,90
Media:            1     17,42      0,00      6,09      0,12      0,00     76,37

Como decía antes, una fila para cada CPU, en este caso un equipo con 2 CPU, únicamente una ejecución:

$ sar -P ALL 2 1
Linux 2.6.28-17-generic (sistemas) 	22/01/12 	_i686_	(2 CPU)

19:31:12        CPU     %user     %nice   %system   %iowait    %steal     %idle
19:31:14        all     17,69      0,00      3,93      0,74      0,00     77,64
19:31:14          0     22,39      0,00      2,99      0,00      0,00     74,63
19:31:14          1     13,04      0,00      5,31      1,45      0,00     80,19

Media:          CPU     %user     %nice   %system   %iowait    %steal     %idle
Media:          all     17,69      0,00      3,93      0,74      0,00     77,64
Media:            0     22,39      0,00      2,99      0,00      0,00     74,63
Media:            1     13,04      0,00      5,31      1,45      0,00     80,19

Mostrar toda la información almacenada en el fichero de estadísticas diario

$ sar -A

Sar no se limita únicamente a la monitorización de CPU, también podemos controlar la actividad en discos (al estilo iostat), de red, procesos, load average, etc. En este primer artículo no hemos centrado en la CPU, en los próximos veremos algunos ejemplos de ello.

Añadir la cabecera Content-type al usar el comando mail o mailx

En algunas versiones del comando mail|mailx|Mail (creo que en las antiguas) se podía especificar una cabecera con el parámetro -a:

$ mail --help
usage: mail [-dEIinv] [-a header] [-b bcc-addr] [-c cc-addr] [-s subject] to-addr ...

De este modo era sencillo especificar cualquier cabecera y modificarla, como por ejemplo el Content-type si quisieramos enviar un correo en formato HTML (text/html). En cambio en nuevas versiones este parámetro ha desaparecido, y si queremos añadir el Content-type, por ejemplo, hay que engañar al MTA del siguiente modo:

$ cat mensaje.html | mail -s "$(echo -e "Este es el asunto\nContent-Type: text/html")" foo@bar.com

Como véis, a la hora de especificar el subject del mail con el parámetro -s, hacemos la trampa de crear un salto de línea y especificar después el Content-type, al igual que podemos ver en las cabeceras de un email estándar. De este modo, el MTA no asocia la segunda línea con el Subject sino como si fuera una cabecera normal de Content Type.

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 ;)