sexta-feira, 25 de julho de 2014

Pesquisa de texto com grep

O comando grep é usado para fazer pesquisa de textos em arquivos e em saída de comandos. Sua forma mais simples é a seguinte.

$ grep ricardo /etc/passwd
ricardo:x:1000:1000:Ricardo Lino Olonca,,,:/home/ricardo:/bin/bash
ftp:x:155:65534::/home/ricardo/Copy/Copy/:/bin/false

Aqui estou procurando todas as linhas que contenham o texto "ricardo". Eu posso também usar o grep para filtrar a saída de um comando.

$ ls -l | grep root
-rw-r--r-- 1 ricardo root    2232995840 Jul  8 09:11 Olonca_2014.iso

Nesse caso eu executei o "ls -l" e pedi ao grep para mostrar somente as linhas que continham a string de texto "root". 

Grep é case sensitive, ou seja, ele faz diferenciação entre caracteres maiúsculos e minúsculos. No exemplo anterior, vamos supor que eu precise saber as linhas que contenham o texto olonca; se eu digitar:

$ ls -l | grep olonca

não será mostrada nenhuma linha, pois o correto é "Olonca".

$ ls -l | grep Olonca
-rw-r--r-- 1 ricardo root    2232995840 Jul  8 09:11 Olonca_2014.iso

Para evitar isso, use o parâmetro "-i".

$ ls -l | grep -i olonca
-rw-r--r-- 1 ricardo root    2232995840 Jul  8 09:11 Olonca_2014.iso

Há situações onde você quer o inverso, ou seja, as linhas que NÃO contem a texto procurado. Para isso use o "-v".

$ grep -v bash /etc/passwd
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh

Aqui estou listando todos os usuários que não possuem o shell bash. Se eu quiser contar essas linhas eu posso chamar o "wc -l" ou usar o parâmetro -c.

$ grep -v bash -c /etc/passwd
76

$ grep -v bash /etc/passwd | wc -l
76

Eu posso também querer mostrar apenas as duas primeiras ocorrências da pesquisa (-m 2)

$ grep -vm2 bash /etc/passwd 
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh

Há dois parâmetros que funcionam como âncoras. Um se refere ao começa da linha (^) e o outro ao final ($). Por exemplo, para mostrar todas as linhas que começam com o texto "ricardo" no arquivo /etc/passwd digite:

# grep ^ricardo /etc/passwd
ricardo:x:1000:1000:Ricardo Lino Olonca,,,:/home/ricardo:/bin/bash

Para ver todos os usuários que usam o login "/bin/false" é só pesquisar pelo texto no final da linha.

$ grep false$ /etc/passwd
usbmux:x:101:46:usbmux daemon,,,:/home/usbmux:/bin/false
messagebus:x:102:104::/var/run/dbus:/bin/false
Debian-gdm:x:103:107:Gnome Display Manager:/var/lib/gdm3:/bin/false
haldaemon:x:105:109:Hardware abstraction layer,,,:/var/run/hald:/bin/false

Aqui vai uma dica preciosa: linhas em branco não consideradas linhas que começam e logo em seguida terminam, sem qualquer caracter entre o início e o fim. Portanto, para eliminar as linhas em branco de um arquivo é só executar o comando abaixo:

$ grep -v ^$ /etc/mtools.conf 
# Debian default mtools.conf file.
# "info mtools" or "man mtools.conf" for more detail.
# # Linux floppy drives
drive a: file="/dev/fd0" exclusive
drive b: file="/dev/fd1" exclusive
drive d: file="/dev/cdrom" exclusive
# # First SCSI hard disk partition
# drive c: file="/dev/sda1"
# # First IDE hard disk partition
# drive c: file="/dev/hda1"
# # dosemu hdimage.
drive m: file="/var/lib/dosemu/hdimage.first" partition=1 offset=128
# # dosemu floppy image
drive n: file="/var/lib/dosemu/fdimage"
# # SCSI zip disk
# drive z: file="/dev/sda4"
# # uncomment the following line to display all file names in lower
# # case by default
# mtools_lower_case=1

Para retirar os comentários do arquivo acima é só não mostrar as linhas que começam com "#". 

# grep -v ^$ /etc/mtools.conf | grep -v ^#
drive a: file="/dev/fd0" exclusive
drive b: file="/dev/fd1" exclusive
drive d: file="/dev/cdrom" exclusive
drive m: file="/var/lib/dosemu/hdimage.first" partition=1 offset=128
drive n: file="/var/lib/dosemu/fdimage"

Você pode querer ver não só as linhas pesquisadas como também algumas linhas antes (-B) e depois (-A).

# dmesg  | grep -A 2 -B 2 libata
[    1.544560] input: AT Translated Set 2 keyboard as /devices/platform/serio0/input/input0
[    1.544897] usbcore: Unknown symbol usb_speed_string (err 0)
[    1.547299] libata version 3.00 loaded.
[    1.745056] e1000e 0000:00:19.0: eth0: (PCI Express:2.5GT/s:Width x1) 50:e5:49:fc:de:9b
[    1.745059] e1000e 0000:00:19.0: eth0: Intel(R) PRO/1000 Network Connection

Você também pode usar as expressões regulares na pesquisa, mas isso é assunto para outro post.