1. http://olinux.uol.com.br/artigos/304/print_preview.html
Curso de Shell - Aula V
Por: Alex Borro ( 24/04/2001 )
Nesta aula teremos um breve tutorial sobre o Grep. Ele não é somente um dos comandos mais úteis,
mas o seu domínio abre portas para dominar outros poderosos comandos, como o sed (que trataremos
na próxima aula) , awk, perl, etc.
Eu fiz uma adaptação/ modificação de um tutorial que eu tenho, e por sinal é excelente, sobre o Grep e
Expressões Regulares em inglês. Espero que tenha ficado legal e vocês gostem.
O que ele faz?
O grep basicamente faz buscas. Mais precisamente:
grep palavra file retorna todas as linhas do arquivo file
que contenham palavra
Outro jeito de usar o grep é atraves de pipe (lembram dos pipes?). Por exemplo:
ls | grep palavra
Lista todos os arquivos que contenham palavra em seu nome. Ou seja, a entrada do grep é uma lista de
arquivos (gerada pelo ls) que será filtrada, sendo impressas somente as linhas que contenham palavra.
Usando caracteres coringas
Suponho que todos saibam o que são caracteres coringas. Caso contrário, coringas são caracteres
especiais que substituem outros. Geralmente o caracter "*" é um coringa que significa "qualquer
caracter em qualquer quantidade". Por isso se a gente executar "ls *", onde "*" entra no lugar do
"nome do arquivo", significando qualquer string de qualquer tamanho. Por isso ele lista todos os
arquivos.
Mas agora voltemos ao grep. Será que ele aceita coringas ??? A resposta é mais do que sim. O grep
suporta algo que vai além de coringas, ele suporta Expressões Regulares. Mas vamos começar apenas
com coringas. Um dos mais usados com o grep é o "." Vamos a um exemplo:
>cat file >grep b.g
file
big big
bad bug bad bug
bigger bigger
boogy
Note que boogy não casa, desde que "." significa "qualquer e apenas um caracter". Para significar
strings arbitrárias utilizamos "*", que funciona da seguinte maneira:
"A expressão consistindo de um caracter seguido por um * casa com qualquer número (inclusive zero)
de repetições desse caracter. Em particular, ".*" significa qualquer string."
Para compreendermos vamos a mais exemplos:
>cat file
big
bad bug
bag
bigger
boogy>grep b.*g file
big
bad bug
bag
bigger
2. boogy>grep b.*g. File
bigger
boogy>grep ggg* file
bigger
Avançando para expressões regulares
Os coringas são o começo, mas a idéia vai mais longe. Por exemplo, suponha que queremos uma
expressão que case com Frederic Smith ou Fred Smith, ou seja, a string "eric" é opcional.
Primeiro, introduzimos o conceito de um "caracter escapado (escaped character)".
"Um caracter escapado é um caracter precedido por uma barra invertida ( ). Essa barra invertida faz o
seguinte: (a) Remove qualquer significado especial do caracter. (b) acrescenta um significado especial a
um caracter que não tenha um significado especial."
Parece complicado, mas veja nos exemplo que é um tanto quanto simples:
Para procurar uma linha contento o texto "hello.gif", o comando correto seria:
grep 'hello.gif' file
Desde que "grep 'hello.gif' file" iria resultar linhas contendo: hello-gif , hello1gif , helloagif , etc.
Ou seja, a barra invertida remove o significado especial do ".", passando esse a significar um simples
ponto ao invés de um coringa.
Agora vamos passar para expressões agrupadas, a fim de encontrar um jeito de criar uma expressão
que case com Frederic ou Fred. Primeiro vamos começar com o operador "?":
"Uma expressão consistindo de caracter seguido por uma interrogação escapada ( ? ) casa com zero
ou uma instância daquele caracter".
Exemplo:
"bugg?y" casa com o seguinte: bugy , buggy mas não com
bugggy
neo@matrix:~$ echo bugy | grep "bugg?y"
bugy
neo@matrix:~$ echo bugggy | grep "bugg?y"
neo@matrix:~$
Agora vamos para expressões agrupadas. No nosso exemplo, queremos tornar opcional a string "eric"
após "Fred", ou seja, não apenas um caracter mas sim um conjunto de caracteres (string).
"Uma expressão dentro de parênteses escapados é tratada como um único caracter"
Exemplos:
"Fred(eric)?" Smith casa com "Fred Smith" or "Frederic Smith"
(abc)* casa com abc , abcabcabc, etc (isto é, qualquer
número de repetições da string "abc", incluindo
zero).
Note que temos que tomar cuidado quando nossas expressões contém espaços em branco. Quando eles
aparecem, precisamos colocar a expressão entre aspas, para que o shell não interprete mal nosso
comando. Para o exemplo acima:
grep "Fred(eric)? Smith" file
Eu aconselho fortemente a sempre usar aspas, mesmo que não usemos espaços em brancos. Já tive
muita dor de cabeça porque expressões e comandos não funcionavam simplesmente por não estarem
entre aspas. Um dos exemplo acima (o do bugg?y) não funciona no meu sistema se não estiver entre
3. aspas. Veja:
neo@matrix:~$ echo bugy | grep "bugg?y"
bugy
neo@matrix:~$ echo bugy | grep bugg?y
neo@matrix:~$
Outros operadores úteis
Para casar algum caracter de uma lista, use [ ] Veja:
"[Hh]ello" casa com linhas contendo "hello" ou "Hello"
Faixas de caracteres também são permitidos:
[0-3] é o mesmo que [0123]
[a-k] é o mesmo que [abcdefghijk]
[A-C] é o mesmo que [ABC]
[A-Ca-k] é o mesmo que [ABCabcdefghijk]
Existem também algumas formas alternativas:
[[:alpha:]] é o mesmo que [a-zA-Z]
[[:upper:]] é o mesmo que [A-Z]
[[:lower:]] é o mesmo que [a-z]
[[:digit:]] é o mesmo que [0-9]
[[:alnum:]] é o mesmo que [0-9a-zA-Z]
[[:space:]] casa com qualquer quantidade de espaços,
inclusive tabulações
Essas formas alternativas, como [[:digit:]] é preferível ao método direto, [0-9].
Os [ ] podem ser usado para indicar caracteres que NÃO devem estar na expressão. É só colocar o
sinal ^ na primeira posição da lista. Veja:
neo@matrix:~$ echo hello | grep "[Hh]ello"
hello
neo@matrix:~$ echo hello | grep "[^Hh]ello"
neo@matrix
Outro exemplo, um pouco mais complicado:
grep "([^()]*)a" file retorna qualquer linha contendo um par de parentes seguido por "a" e que NÃO
contenham outros parênteses dentro. Assim, ele casa com essas linhas:
(hello)a
(aksjdhaksj d ka)a
Mas não com: x=(y+2(x+1))a
Casando com um número especifico de repetições
Suponha que você queira casar um número específico de repetições de uma expressão. Um bom
exemplo são números de telefones. Você pode procurar por um número de telefone com sete dígitos,
assim:
grep "[:digit:]{3}[ -]?[:digit:]{4}" file
Ou seja, três dígitos, opcionalmente um espaço ou um hífen e mais 4 dígitos.
Procurando por começo e fim de linha:
Isso é muito interessante. Digamos que você queira procurar por linhas que contendo espaços em
brancos no começo da linha, a palavra hello e então o fim da linha. Vamos começar com este exempo:
4. >cat file
hello
hello world
hhello
>grep hello file
hello
hello world
hhello
Isso não é o que nós queremos. O que está errado ? O problema é que o grep procura por linhas
contendo a string hello e todas as linhas especificadas contem ela. Para contornar isso, introduzimos os
caracteres de começo e fim de linha:
"O caracter ^ significa começo de linha e o $ significa fim da linha"
Retornando ao nosso exemplo:
grep "^[[:space:]]*hello[[:space:]]*$" file
Ou seja, o começo da linha, qualquer quantidade de espaço em branco (inclusive zero), a palavra hello,
qualquer quantidade de espaço em branco e o fim da linha. Essa expressão faz o que a gente quer,
retornando somente a linha que contém a palavra hello. Outro exemplo:
grep "^From.*Alex" /var/spool/mail/neo
Procura no meu inbox por mensagens de uma pessoa em particular (no caso, Alex). Esse tipo de
expressão regular é extremamente útil e filtros de e-mail, como o procmail, utilizam isso para fazerem
tudo.
Isso ou Aquilo: Procurando uma coisa OU outra:
"A expressão consistindo de duas expressões separadas pelo operador OU | casa linhas contendo uma
das duas expressões"
Note que você DEVE colocar a expressão dentro de aspas simples ou duplas:
grep "cat|dog" file casa com linhas contendo a palavra "cat" ou a
palavra "dog"
grep "I am a (cat|dog)" casa com linhas contendo a string "I am
a cat" ou a string "I am a dog".
Usando backreference (referencia anterior)
Digamos que você queira procurar strings que contenham uma substring em mais de um lugar. Um
exemplo é as tags de cabeçalhos de HTML. Suponha que eu queira procurar por "<H1>alguma
string</H1>". Isto é fácil de se fazer. Mas suponha que eu queira fazer o mesmo, mas permita H2 H3
H4 H5 e H6 no lugar de H1. A expressão <H[1-6]>.*</H[1-6]> não é boa, desde que casa com
"<H1>alguma string</H3>" e nos queremos que a tag de abertura seja igual a de fechamento. Para
fazermos isso, usamos backreference:
"A expressão n onde n é um número, casa com o conteúdo do n-ésimo conjunto de parênteses na
expressão".
Nossa... isso realmente precisa de um exemplo!!!!
<H([1-6])>.*</H1> faz o que nos queríamos fazer acima...
"O Senhor (dog|cat) e a senhora 1 foram visitar o Senhor (dog|cat) e a senhora 2"
Esse é outro exemplo bobo. Os casais tem sempre que serem iguais. Ou um casal de cachorro ou de
gato.
Alguns detalhes cruciais: caracteres especiais e aspas
5. Caracteres Especiais:
Aqui nós descrevemos os caracteres especiais para RegExp (expressões regulares) no grep. Note que
no "egrep", que usa expressões regulares estendidas (atualmente não tem nenhuma funcionalidade a
mais que as expressões regulares normais do GNU grep), a lista de caracteres especiais são os
mesmos, diferindo somente que alguns não precisar estar "escapados".
Os seguintes caracteres são considerados especiais, e precisam estar escapados:
? . [ ] ^ $
Note que o sinal de $ perde seu sentido se tiver caracteres depois dele, do mesmo jeito que o sinal ^
perde seu sentido se tiver caracteres antes dele. Os colchetes [ ] comportam-se um pouco diferente. A
baixo segue as regras para eles:
q O colchete direito ( ] ) perde seu sentido especial se colocado no começo de uma lista, por
exemplo: "[]12]" casa com ] , 1, or 2.
q Um hífen perde seu significado especial se colocado por último. Assim, [15-] casa com 1, 5 ou -
q O caracter ^ perde seu sentido se não for colocado em primeiro lugar.
q A maioria dos caracteres especiais perdem seu significado especial se forem colocados dentro de
colchetes.
Aspas:
Em primeiro lugar, aspas simples são mais seguras de usar porque elas protegem suas expressões
regulares de serem alteradas pelo bash (como foi visto nas aulas anteriores). Por exemplo, grep "!" file
vai produzir um erro, já que o shell pensa que "!" está se referindo ao comando de histórico do shell,
enquanto grep '!' file funciona perfeitamente.
Quando você deve usar aspas simples ? A resposta é: se você precisa usar variáveis do shell, use
aspas duplas. Caso contrário, use aspas simples. Por exemplo:
grep "$HOME" file
Procura em file pelo nome do seu diretório pessoal, enquanto grep '$HOME' file procura pela string
$HOME.
Sintaxe das expressões regulares estendidas
Agora vamos ver a sintaxe do egrep em contraste com a sintaxe do grep. Ironicamente, apesar do
nome "estendido", o egrep atualmente tem menos funcionalidade do que quando foi criado, para manter
a compatibilidade com o tradicional grep. A melhor maneira de fazer um grep estendido é utilizar grep -
E que usa a sintaxe de expressões regulares estendidas sem perda de funcionalidade.
Bom, espero que vocês tenham tido uma boa idéia de como funcionam as expressões regulares. Elas
são extremamente importante, principalmente para utilizar o grep e o poderosíssimo sed, do qual
trataremos na próxima aula. Não se preocupem se estiverem confuso com as expressões, quando
começarem a utilizá-las, as idéias vão clareando. Até a próxima.
Copyright (C) 1999- 2 0 0 0 Linux Solutions