Linux – Dominando o comando grep

O Linux possui muitos comandos nativos que são verdadeiros canivete-suiço para SysAdmins e criação de poderosos scripts.

Iniciei uma serie de posts sobre Shell Script, e agora irei começar a utilizar diversos comandos do Linux para tornar e dar vida a grandes funções com estes scripts, para isso, começarei a abordar separadamente os mais conhecidos e usados.

Neste post vou falar sobre o grep, muitos não sabem mas o significado do nome é (Globally Search a Regular Expression and Print), o nome já diz tudo o que esse comando faz, e deixando mais claro a ideia é procurar texto em uma string ou dentro de arquivos e mostrar linhas, ocorrências, usar combinações para pesquisar e o resultado da pesquisa ser mostrado na tela.

Você não precisa se preocupar em instalar o grep, pois ele já esta ae no seu Linux, para confirmar e verificar a versão veja o comando abaixo:

$ grep
Usage: grep [OPTION]... PATTERN [FILE]...
Try 'grep --help' for more information.
$ grep -V
grep (GNU grep) 2.16
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written by Mike Haertel and others, see <http://git.sv.gnu.org/cgit/grep.git/tree/AUTHORS>.

Digitando apenas o comando e dando enter ele não faz nada, porém mostra um exemplo para uso, e com o parâmetro -V a versão atual, no meu caso 2.6.

Agora segue os parâmetros que iremos utilizar e suas funções:

-c Conta quantas vezes apareceu a string que esta pesquisando
-v Mostra na tela “tudo” menos onde houver a ocorrência da string pesquisada
-i Realiza uma busca pela string ignorando o case, sendo case-insensitive
-o Ira mostrar na tela apenas as ocorrências da string pesquisada ignorando o resto
-n Ira mostrar na tela na primeira coluna a linha onde encontrou a string pesquisada
-B Numero de linhas a serem impressas antes da linha que contem a string pesquisada [BEFORE]
-A Numero de linhas a serem impressas na tela depois da encontrar a linha com a string [AFTER]
-C Quantidade de linhas antes e depois da linha que contem a string [CONTEXT]
-q Ira procurar pela string informada, porém estará em modo silencioso, nada sera impresso na tela, porém caso encontre o comando encerra com 0, caso não encontre nada será 1
-E Extende o uso de Regex no padrão e combinação, usando logica AND e OR por exemplo
-f Um arquivo com combinações de padrões com Regex, podendo usar varias combinações
-l Mostra somente o nome do arquivo onde foi encontrado a string pesquisada
-L Semelhante ao -v, porque mostra apenas os arquivo que não contem a string informada
-h Pesquisa varias arquivos, diretórios se com -r mas não mostra o nome dos arquivos
-r Ira realizar uma pesquisa recursiva em todos os diretórios a partir do informado
–color Deve-se passar o parâmetro ‘never’ caso não queira que a saída marque com cor a string ou ‘auto’ e ‘always’ para operar conforme necessite. Pode mudar a cor alterando GREP_COLOR, GREP_COLORS no environment

 

Exemplo básico

Agora um exemplo básico e bem didático para uso do comando, para isso vou criar um arquivo chamado palavras.txt e inserir um texto dentro e brincar com o grep.

echo -ne "amor\ncasa\nCasa\nCASA\nRaspberryPI\nRaspberry PI\nRaspberry B PI\nArduino\narduino\nARDUINO\nIDEArduino\nLinux é o poder\nEu programo Python e você?\n" > palavras.txt

Vamos visualizar nosso arquivo palavras.txt.

$ cat palavras.txt 
amor
casa
Casa
CASA
RaspberryPI
Raspberry PI
Raspberry B PI
Arduino
arduino
ARDUINO
IDEArduino
Linux é o poder
Eu programo Python e você?

Agora vamos usar o grep e pesquisar pela string “Raspberry“, podemos usar de duas maneiras com o cat um pipe e logo em seguida um grep ou diretamente com o comando, a primeira opção é muito utilizada, porém você irá perder performance caso realizar pesquisa em muitos arquivo ou em um arquivo longo, veremos ainda neste post sobre isso.

$ cat palavras.txt | grep "Raspberry"
RaspberryPI
Raspberry PI
Raspberry B PI

$ grep "Raspberry" palavras.txt 
RaspberryPI
Raspberry PI
Raspberry B PI

Caso eu queira contar o numero de ocorrências da string “Raspberry”:

$ grep -c "Raspberry" palavras.txt 
3

Se eu quiser ver tudo menos a string que contenham “Raspberry”:

$ grep -v "Raspberry" palavras.txt 
amor
casa
Casa
CASA
Arduino
arduino
ARDUINO
IDEArduino
Linux é o poder
Eu programo Python e você?

Agora quero pesquisar pela string “arduino”.

$ grep "arduino" palavras.txt 
arduino

Temos Arduino escrito de diversas maneiras, então vamos pedir para ser case-insensitive.

$ grep -i "arduino" palavras.txt 
Arduino
arduino
ARDUINO
IDEArduino

E se no lugar de mostrar a linha inteira ou o que estiver junto eu mostrar apenas a string procurada.

$ grep -o "arduino" palavras.txt 
arduino
$ grep -oi "arduino" palavras.txt 
Arduino
arduino
ARDUINO
Arduino
$ grep -oi "Raspberry" palavras.txt 
Raspberry
Raspberry
Raspberry

Se eu precisar saber o numero da linha onde foi encontrada a string.

$ grep -n "Raspberry" palavras.txt 
5:RaspberryPI
6:Raspberry PI
7:Raspberry B PI
$ grep -n "duino" palavras.txt 
8:Arduino
9:arduino
11:IDEArduino

Agora vamos pesquisar pela string “arduino” e obter também as 2 linhas antes da string encontrada.

$ grep "arduino" -B 2 palavras.txt 
Raspberry B PI
Arduino
arduino

O mesmo podemos fazer obtém as linhas depois da linha com a string pesquisada.

$ grep "arduino" -A 2 palavras.txt 
arduino
ARDUINO
IDEArduino

E podemos unir as duas opções, pegando e imprimindo linhas antes e depois da linha que contem a string pesquisada.

$ grep "arduino" -C 2 palavras.txt 
Raspberry B PI
Arduino
arduino
ARDUINO
IDEArduino

Caso não queira mostrar nada na tela, só saber se teve sucesso ou não na pesquisa.

$ grep -q "arduino" palavras.txt
$ echo $?
0

$ grep -q "Beaglebone" palavras.txt
$ echo $?
1

Apenas reforçando no exemplo acima pesquisei a string “arduino” com o parâmetro -q (modo silencioso) e peguei a saída do ultimo comando executado com (echo $?), logo em seguida pesquisei por “Beaglebone” como não existe a saída foi 1.

 

Exemplo intermediário

Agora vamos subir um nível e brincar com outros parâmetros. Desta vez vamos criar mais 2 arquivos sistema.txt e hardware.txt, e também copiar a saída do dmesg para dmesg.log e brincar com estes caras.

Preparando os arquivos:

$ echo -ne "Linux Ubuntu\nLinux Debian\nLinux Mint\nLinux CentOS\nRaspbian\nYocto RaspberryPI\nBuildroot RaspberryPI\n" > sistema.txt

$ echo -ne "ARM 1176JZF\mARM Cortex-A7\nBCM2835\nBCM2836\nBeaglebone Black\nAM3358\n" > hardware.txt

$ dmesg > dmesg.log

Eu criei sistema.txt e hardware.txt com palavras aleatórias, você pode agregar mais palavras para realizar seus testes.

Agora eu quero pesquisar em qualquer arquivo e que contenha a string “Raspberry”.

$ grep "Raspberry" *
palavras.txt:RaspberryPI
palavras.txt:Raspberry PI
palavras.txt:Raspberry B PI
sistema.txt:Yocto RaspberryPI
sistema.txt:Buildroot RaspberryPI

$ grep "Raspberry" ./*
./palavras.txt:RaspberryPI
./palavras.txt:Raspberry PI
./palavras.txt:Raspberry B PI
./sistema.txt:Yocto RaspberryPI
./sistema.txt:Buildroot RaspberryPI

Os demais parâmetros anteriores se aplicam aqui também.

$ grep -n "Raspberry" *
palavras.txt:5:RaspberryPI
palavras.txt:6:Raspberry PI
palavras.txt:7:Raspberry B PI
sistema.txt:6:Yocto RaspberryPI
sistema.txt:7:Buildroot RaspberryPI

Agora se eu criar um diretório exemplo/ e mover o palavras.txt para ele será que vai encontrar a string “Raspberry” nele ainda?

$ mkdir exemplo && mv palavras.txt exemplo/
$ grep "Raspberry" *
grep: exemplo: Is a directory
sistema.txt:Yocto RaspberryPI
sistema.txt:Buildroot RaspberryPI

Ele avisa que existe um diretório onde esta sendo feita a pesquisa, para que ele acesse o(s) diretório(s) deve-se passar o parâmetro -r para recursividade.

$ grep -r "Raspberry" *
exemplo/palavras.txt:RaspberryPI
exemplo/palavras.txt:Raspberry PI
exemplo/palavras.txt:Raspberry B PI
sistema.txt:Yocto RaspberryPI
sistema.txt:Buildroot RaspberryPI

As vezes só interessa saber a ocorrências mas não o arquivo.

$ grep -hr "Raspberry" *
RaspberryPI
Raspberry PI
Raspberry B PI
Yocto RaspberryPI
Buildroot RaspberryPI

Se caso queira apenas saber qual arquivo contem a string mas não precisa mostrar ela.

$ grep -lr "Raspberry" *
exemplo/palavras.txt
sistema.txt

E se quiser saber os arquivos que não possuem a string pesquisada.

$ grep -Lr "Raspberry" *
dmesg.log
hardware.txt

Habilitando ou não o uso da saída colorida.

$ grep -r --color=always "Raspberry" *
exemplo/palavras.txt:RaspberryPI
exemplo/palavras.txt:Raspberry PI
exemplo/palavras.txt:Raspberry B PI
sistema.txt:Yocto RaspberryPI
sistema.txt:Buildroot RaspberryPI

$ grep -r --color=never "Raspberry" *
exemplo/palavras.txt:RaspberryPI
exemplo/palavras.txt:Raspberry PI
exemplo/palavras.txt:Raspberry B PI
sistema.txt:Yocto RaspberryPI
sistema.txt:Buildroot RaspberryPI

 

Exemplo avançado

Vamos dar mais um passo sobre esse incrível comando, só que agora com o básico de Expressões Regulares, e como exemplo usaremos o dmesg.log gerado acima.

Fazendo uma busca simples pela string “usb”.

$ grep "usb" dmesg.log 
[    0.668550] usbcore: registered new interface driver usbfs
[    0.668558] usbcore: registered new interface driver hub
[    0.668582] usbcore: registered new device driver usb
[    1.996732] usb usb1: New USB device found, idVendor=1d6b, idProduct=0002
[    1.996735] usb usb1: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[    1.996737] usb usb1: Product: EHCI Host Controller
[    1.996739] usb usb1: Manufacturer: Linux 3.13.0-37-generic ehci_hcd
[    1.996741] usb usb1: SerialNumber: 0000:00:1d.0
[    1.997338] usb usb2: New USB device found, idVendor=1d6b, idProduct=0002
[    1.997340] usb usb2: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[    1.997342] usb usb2: Product: xHCI Host Controller
[    1.997344] usb usb2: Manufacturer: Linux 3.13.0-37-generic xhci_hcd
[    1.997346] usb usb2: SerialNumber: 0000:00:14.0
[    2.000099] usb usb3: New USB device found, idVendor=1d6b, idProduct=0003
[    2.000101] usb usb3: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[    2.000103] usb usb3: Product: xHCI Host Controller
[    2.000105] usb usb3: Manufacturer: Linux 3.13.0-37-generic xhci_hcd
[    2.000107] usb usb3: SerialNumber: 0000:00:14.0
[    2.308561] usb 1-1: new high-speed USB device number 2 using ehci-pci
[    2.440791] usb 1-1: New USB device found, idVendor=8087, idProduct=8000
[    2.440794] usb 1-1: New USB device strings: Mfr=0, Product=0, SerialNumber=0
[    2.712387] usb 1-1.5: new full-speed USB device number 3 using ehci-pci
[    2.805614] usb 1-1.5: New USB device found, idVendor=0cf3, idProduct=0036
[    2.805616] usb 1-1.5: New USB device strings: Mfr=0, Product=0, SerialNumber=0
[    2.880293] usb 1-1.7: new high-speed USB device number 4 using ehci-pci
[    2.972951] usb 1-1.7: New USB device found, idVendor=0bda, idProduct=0129
[    2.972954] usb 1-1.7: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[    2.972956] usb 1-1.7: Product: USB2.0-CRW
[    2.972958] usb 1-1.7: Manufacturer: Generic
[    2.972959] usb 1-1.7: SerialNumber: 20100201396000000
[    3.044205] usb 1-1.8: new high-speed USB device number 5 using ehci-pci
[    3.201201] usb 1-1.8: New USB device found, idVendor=0c45, idProduct=64af
[    3.201203] usb 1-1.8: New USB device strings: Mfr=2, Product=1, SerialNumber=0
[    3.201205] usb 1-1.8: Product: Laptop_Integrated_Webcam_HD
[    3.201206] usb 1-1.8: Manufacturer: CN0Y3PX8724873AGB17FA01
[   14.243360] usbcore: registered new interface driver btusb
[   14.274681] usbcore: registered new interface driver rts5139
[   14.624063] input: Laptop_Integrated_Webcam_HD as /devices/pci0000:00/0000:00:1d.0/usb1/1-1/1-1.8/1-1.8:1.0/input/input13
[   14.624169] usbcore: registered new interface driver uvcvideo
[   14.761434] usbcore: registered new interface driver ath3k
[   14.781788] usb 1-1.5: USB disconnect, device number 3
[   14.981529] usb 1-1.5: new full-speed USB device number 6 using ehci-pci
[   20.075906] usb 1-1.5: New USB device found, idVendor=0cf3, idProduct=0036
[   20.075911] usb 1-1.5: New USB device strings: Mfr=0, Product=0, SerialNumber=0

Bastante coisa não é? Vamos trabalhar em cima extendendo os recursos de Regex da nossa expressão, por exemplo quero somente as linhas que contenham usb2 OU usb3, aplicando a logica OR.

$ grep -E "usb2|usb3" dmesg.log 
[    1.997338] usb usb2: New USB device found, idVendor=1d6b, idProduct=0002
[    1.997340] usb usb2: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[    1.997342] usb usb2: Product: xHCI Host Controller
[    1.997344] usb usb2: Manufacturer: Linux 3.13.0-37-generic xhci_hcd
[    1.997346] usb usb2: SerialNumber: 0000:00:14.0
[    2.000099] usb usb3: New USB device found, idVendor=1d6b, idProduct=0003
[    2.000101] usb usb3: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[    2.000103] usb usb3: Product: xHCI Host Controller
[    2.000105] usb usb3: Manufacturer: Linux 3.13.0-37-generic xhci_hcd
[    2.000107] usb usb3: SerialNumber: 0000:00:14.0

Agora, vamos pesquisar por uma linha que contenha “usb” E tambem “Product:”, vamos aplicar a logica AND.

$ grep -E "usb.*Product:" dmesg.log 
[    1.996737] usb usb1: Product: EHCI Host Controller
[    1.997342] usb usb2: Product: xHCI Host Controller
[    2.000103] usb usb3: Product: xHCI Host Controller
[    2.972956] usb 1-1.7: Product: USB2.0-CRW
[    3.201205] usb 1-1.8: Product: Laptop_Integrated_Webcam_HD

Mas eu quero só com “usb2” ou “usb3” casando com “Product:”.

$ grep -E "usb(2|3).*Product:" dmesg.log 
[    1.997342] usb usb2: Product: xHCI Host Controller
[    2.000103] usb usb3: Product: xHCI Host Controller

Sentiu o poder da ferramenta? E você pode aplicar varias combinações com Expressões Regulares, estudaremos no futuro sobre isso.

Podemos criar um arquivo com nosso Regex e usar ele como padrão, alias, podemos colocar varias combinações neste arquivo.

$ echo "usb(2|3).*Product:" > meu_regex 
$ cat meu_regex 
usb(2|3).*Product:
$ grep -f meu_regex -E dmesg.log 
[    1.997342] usb usb2: Product: xHCI Host Controller
[    2.000103] usb usb3: Product: xHCI Host Controller

 

Usando direto e com pipe

Lembra quando comentei de usar o grep direto e usar ele com qualquer comando um pipe e logo em seguida o grep? Vamos fazer uma pesquisa no /var/log/syslog.1 por quantas ocorrências da string “info” usando cat e o grep direto.

$ time cat /var/log/syslog.1 | grep -c "info"
1027

real	0m0.011s
user	0m0.005s
sys	0m0.008s


$ time grep -c "info" /var/log/syslog.1
1027

real	0m0.009s
user	0m0.009s
sys	0m0.000s

Agora uma dica legal, para desempenho de uso em grande escala do grep é setar LC_ALL=C antes:

$ strace -c grep -c "info" /var/log/syslog.1 
187
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 27.03    0.000160          11        14           mmap
 14.19    0.000084           8        10           read
 13.18    0.000078          13         6           open
 11.15    0.000066           8         8           mprotect
  9.63    0.000057          11         5         5 access
  5.57    0.000033           4         9           fstat
  4.56    0.000027           3         9           close
  3.55    0.000021          11         2           munmap
  3.55    0.000021           7         3           brk
  3.04    0.000018          18         1           execve
  1.52    0.000009           9         1           write
  1.35    0.000008           4         2         1 ioctl
  1.35    0.000008           8         1           openat
  0.34    0.000002           2         1           arch_prctl
------ ----------- ----------- --------- --------- ----------------
100.00    0.000592                    72         6 total

$ export LC_ALL=C

$ strace -c grep -c "info" /var/log/syslog.1 
187
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 20.07    0.000114          11        10           read
 16.73    0.000095           8        12           mmap
 16.20    0.000092          12         8           mprotect
  6.87    0.000039           8         5         5 access
  6.16    0.000035           9         4           open
  6.16    0.000035          18         2           munmap
  5.99    0.000034          34         1           execve
  5.46    0.000031          10         3           brk
  4.40    0.000025           4         7           close
  3.87    0.000022           3         7           fstat
  2.99    0.000017          17         1           write
  2.64    0.000015          15         1           openat
  1.94    0.000011           6         2         1 ioctl
  0.53    0.000003           3         1           arch_prctl
------ ----------- ----------- --------- --------- ----------------
100.00    0.000568                    64         6 total

Novamente verificando o tempo apos setar LC_ALL=C.

$ time grep -c "info" /var/log/syslog.1    
187

real	0m0.003s
user	0m0.003s
sys	0m0.000s


$ time cat /var/log/syslog.1 | grep -c "info"
187

real	0m0.005s
user	0m0.000s
sys	0m0.009s

No meu syslog.1 não deu tanta diferença, mas fazer essa varredura em um aquivo de 500M você notara uma grande diferença.

Conhecemos, aprendemos e devoramos o grep, vimos o grande potencial que ele pode nos oferecer, ainda possui mais recursos e parâmetros que não vimos e pode ser visto com man grep, mas o foco e suas principais funções foi exemplificado aqui.

Espero que tenham gostado e até a próxima!

Share Button

CC BY-NC-SA 4.0 Linux – Dominando o comando grep by Cleiton Bueno is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.