Prompt-doc: Expressões Regulares - Introdução

Conectiva - Curitiba, 17 de Dezembro de 2003

Este é o histórico da linha de comando de uma palestra que fiz para funcionários da Conectiva, sobre Expressões Regulares. O objetivo era apresentar todos os metacaracteres básicos e sua utilidade.

  • Data: 17-Dez-2003
  • Local: Auditório da Conectiva, Curitiba
  • Duração: 2 horas
  • Requisitos: Nenhum

Os Metacaracteres

$ #         ^ $ + * {} () | ? [] .

O arquivo de exemplo

$ cd /etc
$ cat passwd

Âncoras para início de fim de linha

$ grep root passwd
$ grep ^root passwd
$ grep bash$ passwd
$ grep sh$ passwd
$ grep 'sh$' passwd          # proteger com aspas

Aspas duplas caso use variáveis

$ zzz=ro
$ grep "$zzzot" passwd
$ grep "${zzz}ot" passwd     # proteger com {}

Use os colchetes para listar possibilidades para uma posição

$ echo Carlos | grep 'Carlos'
$ echo Carlos | grep '[Cc]arlos'
$ echo carlos | grep '[Cc]arlos'
$ echo harlos | grep '[Cchfg]arlos'

Misture os metacaracteres a gosto

$ echo Carlos | grep '^[Cc]arlos$'

A lista serve para lidar com acentuação também

$ vi /tmp/acao.txt                 # acao, ACAO, Açao, aÇãO, AÇao, etc...
$ cat /tmp/acao.txt | grep 'acao'
$ cat /tmp/acao.txt | grep 'a[cç]ao'
$ cat /tmp/acao.txt | grep 'a[cç][aã]o'
$ cat /tmp/acao.txt | grep -i 'a[cç][aã]o'
$ cat /tmp/acao.txt | grep '[Aa][CÇcç][AÃaã][Oo]'  # todas possibilidades

Como casar linhas em branco

$ grep '^$' passwd

Listas negadas e intervalos

$ grep '^[aeiou]' passwd
$ grep '^[bcdfghjklmnpqrstvwxyz]' passwd
$ grep '^[^aeiou]' passwd
$ grep '[^^]' passwd             # negando o chapéuzinho
$ grep '^[a-z]' passwd           # o hífen indica intervalo
$ grep '^[a-]' passwd            # sem letras ao redor, hífen normal
$ grep '^[-z]' passwd
$ grep '^[az-]' passwd
$ grep '^[0-9]' passwd           # linhas iniciadas por números
$ grep '[0-9]' passwd
$ grep '[0-90-9]' passwd         # errado
$ grep '[0-9][0-9]' passwd       # certo
$ grep '[0-9][0-9][0-9]' passwd

Intervalos respeitam a tabela ASCII

$ man ascii

Classes POSIX respeitam o LOCALE

$ echo ááá | grep '[a-z]'
$ echo $LANG
$ echo ááá | grep '[[:lower:]]'
$ echo ááá | grep '[0-9[:lower:]ABC ]'

O curinga: o ponto

$ grep '^[aeiou]' passwd         # começa com vogal
$ grep '^.[aeiou]' passwd        # segunda letra vogal
$ grep '^..[aeiou]' passwd       # terceira letra vogal

Egrep e chaves para repetições

$ grep '^.......................[aeiou]' passwd
$ grep '^.{25}[aeiou]' passwd     # errado
$ grep '^.{5}[aeiou]' passwd      # errado
$ grep '^.\{5\}[aeiou]' passwd    # deve escapar!
$ egrep '^.{5}[aeiou]' passwd     # ou usar o egrep
$ egrep 'f{995}' passwd           # 995 efes
$ egrep 'f{1,995}' passwd         # de 1 a 995 efes

Mais sobre chaves

$ egrep '^.{5}[aeiou]' passwd      # sexta letra vogal
$ egrep '^.{4,5}[aeiou]' passwd    # quinta ou sexta letra vogal
$ egrep '^.{1,5}[aeiou]' passwd    # de segunda a sexta letra vogal
$ egrep '^.{5,}[aeiou]' passwd     # sexta (ou mais) vogal

Linhas de tamanho fixo

$ echo aaaaaaaaaaaaaaaa | egrep 'a{6}'
$ echo aaaaaaaaaaaaaaaa | egrep '^a{6}$'
$ echo aaaaaaaaaaaaaaaa | egrep '^a{6,}$'
$ echo aaaaaaaaaaaaaaaa | egrep 'a{6,}'
$ echo aaaaaaaaaaaaaaaa | egrep 'a{2,6}'

Repetição de listas

$ egrep '[aeiou]{2}' passwd        # duas vogais seguidas
$ egrep '^.[aeiou]{2}' passwd      # segunda e terceira vogais

? * e + são atalhos para as chaves {}

$ #       ?  {0,1}        *  {0,}      +  {1,}

O opcional ?

$ echo mala | egrep mala
$ echo malas | egrep malas?
$ echo mala | egrep malas?
$ echo mala | egrep mal(as)?           # erro!
$ echo mala | egrep 'mal(as)?'         # deve proteger com 'aspas'
$ echo mal | egrep 'mal(as)?'

Não há como negar strings no meio da linha

$ echo mala | egrep '[^l][^a]'
$ echo mala | egrep '(^la)'
$ echo mala | egrep '(^ma)'

Grupos são legais!

$ echo mala | egrep '(((((((((a)))))))))'
$ echo mala | egrep '((((((((((a))))))))))'
$ echo mala | egrep '((((((((((a)))))))))'
$ echo mala | grep '\(\(\(\(\(\(\(\(\(\(a\)\)\)\)\)\)\)\)\)\)'

Os retrovisores (máximo de nove, marca o texto e não a ER)

$ echo mala | egrep '(a)l\1'
$ echo aja | egrep '(a).\1'
$ echo a.a | egrep '(a).\1'
$ echo a.a | egrep '([0-9]).\1'
$ echo 7a7 | egrep '([0-9]).\1'
$ echo 788 | egrep '([0-9])\1\1'      # não casa
$ echo 888 | egrep '([0-9])\1\1'      # casa
$ echo 888 | egrep '([0-9])\1{2}'     # casa

Como procurar palavras repetidas

$ echo "por que que eu sou" | egrep '([a-z]{2,8}) \1'
$ echo "por que qu eu sou" | egrep '([a-z]{2,8}) \1'
$ echo "quero-quero" | egrep '([a-z]{2,8})-\1'

Usando ? * e +

$ echo b | egrep 'a*'
$ echo b | egrep 'a+'
$ echo b | egrep 'a?'
$ echo ab | egrep 'a+'
$ echo aaaaaa | egrep 'a+'
$ echo aaaaaa | egrep '^a+$'
$ echo aaaaaa | egrep '^a*$'

O tudo e o nada: ponto-asterisco .*

$ echo aaaaaa | egrep '.*'
$ echo | egrep '.*'
$ echo dayukafukdfkidrifhiioyohgoyiejeoueou | egrep '.*'

? * e + são gulosos!

$ echo "antes <b>bold<i>ital</i></b> texto" | sed 's/<.*>//g'
$ echo "antes <b>bold<i>ital</i></b> texto" | sed 's/<.\{0,\}>//g'
$ echo "antes <b>bold<i>ital</i></b> texto" | sed 's/<.\{1,\}>//g'
$ echo "antes <b>bold<i>ital</i></b> texto" | sed 's/<.\{2,\}>//g'
$ echo "antes <b>bold<i>ital</i></b> texto" | sed 's/<.\{2\}>//g'
$ echo "antes <b>bold<i>ital</i></b> texto" | sed 's/<.\{1,2\}>//g'
$ echo "antes <b>bold<i>ital</i></b> texto" | sed 's/<[^>]*>//g'

Ou isso ou aquilo

$ egrep 'root|ftp' passwd
$ egrep '^root|FTP' passwd
$ egrep '^(root|FTP)' passwd
$ egrep '^(root|ftp)' passwd
$ egrep '^(root|ftp).*\1' passwd

Procurar por palavras repetidas na mesma linha

$ cat passwd | egrep '(mail).*\1.*\1'
$ cat passwd | egrep '([a-z]{3,}).*\1.*\1'
$ cat passwd | egrep '^([a-z]{3,}).*\1.*\1'
$ cat passwd | egrep '^([a-z]{3,}).*/home/\1'
$ cat passwd | egrep '^([a-z]{3,}).*/var/spool/\1'

Usuários com shell e que o $HOME é diferente do login

$ cat passwd | egrep 'bash$' | egrep -v '^([a-z]{3,}).*/var/spool/\1'
$ cat passwd | egrep 'bash$' | egrep -v '^([a-z]{3,}).*/home/\1'

Ou isso ou aquilo também se aplica a ERs complicadas!

$ cat passwd | egrep '^[aeiou]{2}|bash$'
$ cat passwd | egrep '(^[aeiou]{2})|(bash$)'
$ cat passwd | sed -nr 's/((^[aeiou]{2})|(bash$))/XXXX/gp'

Uma ER bem simples para casar um email

$ #         [A-Za-z0-9._]+@([A-Za-z0-9-]+\.)+[A-Za-z]{2,6}

Trocando a ordem de campos

$ grep root passwd | cut -d: -f1,6
$ grep root passwd | cut -d: -f1,6 | sed -r 's/(.*):(.*)/\2:\1/'
— EOF —

Gostou desse texto? Aqui tem mais.