Incrementando o Shell com Expressões Regulares Rudson Ribeiro Alves - UVV rudsonalves@yahoo.com.br
O que são Expressões Regulares (ERs ou regexes)? ER é uma composição de símbolos, caracteres com funções especiais, que, agrupados entre si e com caracteres literais, formam uma seqüência, uma expressão. Essa expressão é interpretada como uma regra, que indicará sucesso se uma entrada de dados qualquer obedecer exatamente a todas as suas condições. Aurélio Marinho Jargas -http://guia-er.sourceforge.net/ Senta que lá vem a História... Em meados de 1950, o matemático Stephen Kleene descreveu um modelo neural utilizando uma notação matemática chamada de regular sets. Em dezembro de 1967, Ken Thompson transferiu essa notação para um editor chamado QED, e para o editor de Unix ed. Em meados de 1970, os regular sets evoluíram para as Experssões Regulares, como as conhecemos hoje, com a criação do comando grep. Atualmente ERs são empregadas em vários comandos, linguagens e editores: ed, grep, sed, awk, vi, emacs, Perl, PHP, java, mysql, python, find...
Não confunda Metacaracteres com Curingas! Curingas são utilizados para expressar conjuntos de arquivos na linha de comando: *.txt todos os arquivos terminados com.txt arquivo-??.txt dois caracteres qualquer após o hífen arquivo.{txt,html} terminação txt ou html Apresentação Bibliografia Comando grep, sed e tr Conhecendo os metacaracteres
Bibliografia: Comando man do sistema GNU/Linux (grep, sed, tr,...) Aurélio Marinho Jargas (o verde: http://guiaer.sourceforge.net/ http://en.wikipedia.org/wiki/regular_expression Esta apresentação é baseada no material de ERs do Aurélio Marinho Jargas: http://aurelio.net/er/apostila-conhecendo-regex.pdf
Comando grep: grep, egrep, fgrep imprime linhas que casão com uma máscara. grep [opções] MÁSCARA [arquivo] Exemplos: $ grep 'root' /etc/passwd - imprime as linhas de /etc/passwd que contêm a palavra root $ grep -v 'root' /etc/passwd - imprime as linhas de /etc/passwd que não contêm a palavra root $ grep -l 'wireless' /usr/src/linux/documentation/* Imprime os arquivos do diretório /usr/src/linux/documentation/ e subdiretórios, que possuam a palavra 'wireless'
Comando sed sed - editor de linha de comando para filtrar e modificar um texto. sed [opções] 'comandos' [arquivo] Exemplos: $ sed '/root/ d' /etc/passwd - apaga as linhas que possuem a palavra root $ sed '5,10 d' /etc/passwd - remove as linhas de 5 a 10 do arquivo /etc/passwd $ sed 's/root/root/g' /etc/group - substitui todas as ocorrências de root por ROOT
Comando tr tr - translada ou troca caracteres tr [opções] conjunto1 [conjunto2] Exemplos: $ cat /etc/group tr -d : - remove todos os caracteres : de /etc/group $ echo -e [group]\t[pass]\t[gid]\t[users] ; cat /etc/group tr : \t - substitui todos os caracteres : por um tab
Metacaracteres Metacaracteres são caracteres com funções específicas, que informam padrões e posições impossíveis de serem especificadas com caracteres normais. Exemplo: - todas as linhas iniciadas pelos caracteres a, b, c e d, e que terminam com 1 - todas as linhas que possuam três números em seqüência - todos as linhas que possuem um padrão de data (dd/mm/aaaa) -...
O metacaracter circunflexo ^ O ^, representa o início de uma linha. Podemos usá-lo para encontrar todas as linhas iniciadas por uma seqüência de caracteres específicas. Exemplos: $ grep '^root' /etc/passwd - filtra todas as linhas iniciadas pela palavra 'root' $ grep '^a' /etc/passwd - filtra todas as linhas iniciadas pelo caracter 'a' $ sed '/^s/ d' /etc/group - remove todas as linhas iniciadas pelo caracter 's' $ grep -v '^s' /etc/group - o mesmo que o sed acima
O metacaracter cifrão $ O $, representa um fim de uma linha. Podemos usá-lo para encontrar todas as linhas terminadas por uma seqüência de caracteres específicas. Exemplos: $ grep 'root$' /etc/group - filtra todas as linhas terminadas pela palavra 'root' $ grep 'bash$' /etc/passwd - filtra todos os usuários que utilizam o bash como shell padrão Dica do Aurélio: ER para encontrar linhas em branco: ^$ sed '/^$/ d' /etc/profile - remove linhas em branco do arquivo /etc/profile
O metacaracter de lista [] Os [] permitem limitar um conjunto de caracteres a ocupar uma dada posição no texto. Exemplos: $ grep '^[aeiou]' /etc/group - filtra todos os grupos com nome iniciado pelas vogais $ grep '^[bcdfghjklmnpqrstvxywz]' /etc/passwd - filtra todas os usuários cujo o nome inicia por uma consoante $ grep '[0123456789][0123456789][0123456789]' /etc/group - filtra todas as linhas que possuam três números em seqüência $ sed 'y/abcdefghijklmnopqrstuvxywz/abcdefghijklmnopqrdtuvxywz/' / etc/group - transforma o conteúdo do /etc/group em caixa alta
O metacaracter hífen, para intervalo de listas [-] Para lista seqüênciais como 0123456789, abcd...z é possível utilizar o meta caracter -, para simbolizar a seqüência: Exemplos: $ grep '[0-9][0-9][0-9]' /etc/group - filtra todas as linhas que possuam três números em seqüência $ sed 'y/[a-z]/[a-z]/' /etc/group - a opção y do sed considera tudo como caracter. Não irá funcionar como desejado $ cat /etc/group tr [a-z] [A-Z] - transforma o conteúdo do /etc/group em caixa alta, com mais elegância
Metacaracter ponto. O. é um metacaracter que representa qualquer caracter em uma dada posição. Exemplos: $ grep '^.[aeiou]' /etc/group - filtra todas as linhas que iniciam com qualquer caracter, seguido de uma vogal $ grep '^...$' /etc/passwd - filtra todas as linhas que possuem 19 caracteres E se quiser as linhas com 41 caracteres, terá que usar 41 pontos!
Metacaracter chaves {} As {} serve para indicar a quantidade de repetições de um caracter ou metacaracter Exemplos: $ grep '^.\{41\}$' /etc/passwd - filtra todas as linhas que possuem 41 caracteres. Observe que as barras reverssas, \, são necessários para escapar as chaves no grep. É sugerido utilizar o egrep no lugar do grep. $ egrep '^.{41}$' /etc/passwd - mesmo comando acima, porem mais limpo $ egrep '^.{20,40}$' /etc/passwd - filtra as linhas com 20 a 40 caracteres
Metacaracter chaves {} Mais exemplos: $ egrep '^.{20,}' /etc/passwd - filtra as linhas com 20 ou mais caracteres. $ egrep '^.{,27}$' /etc/passwd - filtra as linhas com menos de 27 caracteres Algumas abreviações de repetição: Meta Equivalência Descrição? {0,1} pode aparecer uma vez ou não * {0,} pode aparecer quauqer quantidade, inclusive nenhuma + {1,} aparecer uma vez ou mais $ egrep '^.+$' /etc/profile - filtra as linhas com 1 ou mais caracteres, ou seja, linhas não vazias
Metacaracter.* O metacaracter.* equivale ao curinga * utilizado no comando ls, ou seja, ele representa qualquer caracter em qualquer quantidade. $ egrep '^[aeiou].*bash$' /etc/passwd - filtra todas as linhas iniciadas com uma vogal e terminadas com a palavra bash Separar NOME, VERSÃO e EXTENSÃO do nome de uma fonte: Exemplo: lame-2.93.tar.gz sed => s/(nome)-(versão).(extensão)$/campo CAMPO - 1 - nome, 2 - versão e 3 para extensão s/(.*)-(.*).(.*..*)/... => escapar () e. (ponto mesmo) sed 's/\(.*\)-\(.*\)\.\(.*\..*\)/\1 2 ou 3/' ou sed -r 's/(.*)-(.*)\.(.*\..*)/\1 2 ou 3/' sem escapar os ()
Resolvendo: $ SOURCE= lame-2.93.tar.gz $ echo $SOURCE sed -r 's/(.*)-(.*)\.(.*\..*)/\1/' #nome lame $ echo $SOURCE sed -r 's/(.*)-(.*)\.(.*\..*)/\2/' # versão 2.93 $ echo $SOURCE sed -r 's/(.*)-(.*)\.(.*\..*)/\3/' # extensão tar.gz
Metacaracter ou ( ) As listas, [], trabalham apenas com caracteres, mas em outras ocasiões é necessário procurar por palavras alternativas. Para isto de usa o ou lógico $ egrep '^(root adm lp):' /etc/passwd - filtra apenas as linhas iniciadas com root, adm ou lp Metacaracter de negação de lista [^] Para se negar uma lista, basta colocar o circunflexo a frente dos caracteres negados $ egrep '^[^aeiou]' /etc/passwd - filtra as linhas iniciadas com consoantes, o mesmo que o comando: $ egrep '^[bcdfghjklmnpqrstvxwyz]' /etc/passwd
Quando escapar? Dependendo do aplicativo, as ERs podem ter algumas variações. O Aurélio montou a tabela abaixo para saber quando escapar ou não: Programa Opção mais chaves borda ou grupo awk? + - - () egrep? + {,} \b () grep \? \+ \{,\} \b \ \(\) emacs? + - \b \ \(\) find? + - \b \ \(\) gawk? + {,} \<\> () sed* \? \+ \{,\} \<\> \ \(\) vim \= \+ \{,\} \<\> \ \(\) * o sed possui a opção -r que estende o uso de ERs, evitando a maioria dos escapes
Resumo dos metacaracteres Meta Posicionamento ^ início da linha $ final da linha Lista [abc] casa com os caracteres a, b e c [a-c] casa com os caracteres a, b e c [^abd] não casa com os caracteres a, b e d (um dois) casa com as palavras um e dois Repetições a{2} casa com a letra a duas vezes a{2,5} casa com a letra a duas a cinco vezes a{2,} casa com a letra a duas vezes ou mais a? casa com a letra a zero vezes ou uma a* casa com a letra a zeros vezes ou mais a+ casa com a letra a uma vez ou mais Curingas. casa com qualquer caracter uma vez.* casa com qualquer caracter várias vezes
Hora da diversão: Alguns desafios Desafio 1: Remover as tags HTML de Programacao.html, sem remover <> (diferente). Sugestão: sed -r 's/padrão/substituto/g' Solução com sed -r ou escapar +, \+ $ sed -r 's/<[^>]+>//g' Programacao.html
Desafio 2: Validar datas da lista data.txt O Arquivo data.txt: 13/05/2004 45/04/2003 22/06/1967 02/08/1995 17/09/1068 22/27/1990 12/02/2405 123/02/2000 12/124/2005 06/04/27 13-05-2004 45-04-2003 22-06-1967 17-09-1068 02-08-1995 22-27-1990 12-02-2405 123-02-2000 12-124-2005 06-04-27 13.05.2004 45.04.2003 22.06.1967 17.09.1068 22.27.1990 02.08.1995 12.02.2405 05..02.2000 12.124.2005 06.04.27 Solução da validação de datas com egrep ou escapar os \(\) '^(0[0-9] [12][0-9] 3[01])[-/.](0[1-9] 1[12])[-/.](19 20)[0-9] [0-9]'
Desafio 3: Validar emails da lista emails.txt ze ze@mane ze@mane.com ze@mane.com.br ze@mane.br ze.mane@mane ze.mane@mane.co.uk ze@mane.com.com ze@ ze_mane@mane.com ze-man@mane.com.br @ze.com ze-cezar_mane@antena.com.oque
Solução: Expressão Regular: [a-za-z_-.]+@[0-9a-za-z.-]+\.[a-z]{2,3}$ $ egrep '[a-za-z_-.]+@[0-9a-za-z.-]+\.[a-z]{2,3}$' emails.txt ze@mane.com ze@mane.com.br ze@mane.br ze.mane@mane.co.uk ze@mane.com.com ze_mane@mane.com ze-man@mane.com.br
Diversão garantida com sed: tenho um arquivo texto com 15000 linhas com o seguinte formato: $ cat a.txt 006528SL757317280BR12/09/200513/01/200500000201134702240593000000602005011304 006528SL757317259BR12/09/200513/01/200500000201134702240593000000612005011304 006528SL757317245BR12/09/200513/01/200500000201134702240593000000622005011304 006528SL757317188BR12/09/200513/01/200500000201134702240593000000632005011304 e quero formatar esse arquivo para dar um LOAD DATA LOCAL INFILE no mysql, para tanto quero inserir um tab como separador de campos para separá-los na forma 006528 SL757317280BR 12/09/2005 13/01/2005 000002011347 02240593000000602005011304 sed -r 's/^(.{6})(.{13})(.{10})(.{10})(.{12})(.*)$/ \1\t\2\t\3\t\4\t\5\t\6/' a.txt 006528 SL757317280BR 12/09/2005 13/01/2005 000002011347 02240593000000602005011304 006528 SL757317259BR 12/09/2005 13/01/2005 000002011347 02240593000000612005011304 006528 SL757317245BR 12/09/2005 13/01/2005 000002011347 02240593000000622005011304 006528 SL757317188BR 12/09/2005 13/01/2005 000002011347 02240593000000632005011304
FIM