De acordo com as Leis 12.965/2014 e 13.709/2018, que regulam o uso da Internet e o tratamento de dados pessoais no Brasil, ao me inscrever na newsletter do portal DICAS-L, autorizo o envio de notificações por e-mail ou outros meios e declaro estar ciente e concordar com seus Termos de Uso e Política de Privacidade.


Expr - Processamento de Strings utilizando Expressões Regulares ou RegEx

Colaboração: Alexandre de Abreu

Data de Publicação: 27 de Maio de 2005

O comando expr é muito conhecido pelos programadores shell(bash, ksh, sh, etc.), mas, a sua utilização, na maioria das vezes, restringe-se ao comando abaixo:

  contador=`expr $contador + 1`

Este comando irá incrementar o valor da variável contador em 1. Outras formas de alcançar o mesmo objetivo em Bash seriam:

  let contador++
  contador=$((contador+1))
  contador=$(bc <<< "$contador + 1")

É verdade que existem várias outras maneiras de fazer, mas o foco deste documento é apresentar as funcionalidades do comando expr, um poderoso processador de expressões.

Esse comando pode ser utilizado para processar expresões matemáticas através de operadores lógicos como |, >, >=, &, que representam o OU, E, MAIOR QUE, etc., assim como também pode utilizar operadores aritméticos como visto no exemplo acima com o operador de adição ou +. Outras possibilidades são % ou módulo, * ou multiplicação.

Abaixo veremos exemplos de expressões aritméticas utilizando alguns operadores assim como os parênteses para agrupar e definir a ordem de execução dos comandos:

  $ expr \( 30 + 2 \) \* \( 13 % 2 \) - \( 2 \* \( 20 - 8 \) \)
  8
  $ expr \( 30 + 2 \) \* \( 13 % 2 \) - \( 2 \* \( 20 - 8 \) \) / 2
  20
  $ expr \( \( 30 + 2 \) \* \( 13 % 2 \) - \( 2 \* \( 20 - 8 \) \) \) / 2
  4
  

Todos sabemos que o domínio do tópico Expressões Regulares(RegEx) é de grande valia e utilidade para qualquer programador, principalmente aqueles que utilizam linguagens scripting como Perl, PHP, Shell, Python, Ruby, utilizadas na manipulação de Strings.

O expr possui suporte à RegEx, assim como o comando grep. Validar expressões torna-se um trabalho viável mesmo sem o GNU grep/egrep, ique nem sempre estão disponíveis em algumas versões/sabores do UNIX.

A sintaxe para o processamento de uma String ou validação contra um padrão(pattern) utilizando expressões regulares através do comando expr pode ser feita de duas maneiras:

   expr     STRING : REGEXP
  
   expr     match STRING REGEXP
  

Adotaremos aqui a primeira sintaxe mostrada acima. Para tentarmos entender como funcionam as RegEx juntamente com o comando expr, nada melhor que utilizar exemplos. A seguir incluimos alguns comandos com pequenos comentáros, alguns deles acompanhandos do comando equivalente utilizando o GNU grep:

  1. Cadeias de caracteres ou Strings que começam por D precedidas ou não de um ou mais espaços ou caracteres de tabulação(TAB)

      $ expr " Dicas" : '[[:blank:]]*D'
      2
      
      $ expr "Dicas" : '[[:blank:]]*D'
      1
      
      $ expr "   Dicas" : '[[:blank:]]*D'
      4
      
    
    Primeiramente, deve-se deixar claro que, como na maioria dos casos no mundo UNIX, a String é tratada de modo "Case Sensitive, ou seja ``D é diferente de d.

    O caracter ^, que geralmente determina o início de uma cadeia de caracteres é implícito, ou seja, o expr, por si só, já entende que o padrão acima descrito, utilizando a RegEx '[[:blank:]]*D', é equivalente ao utilizado através do comando grep:

      $ echo "   Dicas" | grep "^[[:blank:]]*D"
        Dicas
      
    
    O comando acima seria o equivalente ao último comando expr mostrado, veja que ele utiliza o caractere ^ para determinar o início da linha ou da cadeia de caracteres.

    Voltando ao comando: expr " Dicas" : '[[:blank:]]*D'

    Verificamos que a saída padrão é igual a 4, pois, de acordo com a String que foi testada contra o padrão em RegEx acima existem 4 caracteres que estão de acordo com a cadeia " Dicas": 3 espaços e o caracter D.

    O expr imprime na saída padrão o número de caracteres que estão de acordo com o padrão testado, no último caso, 4. Veremos logo abaixo como imprimir os caracteres que estão de acordo com o padrão especificado na linha de comando, o que é bem mais interessante e útil.

    Vejam que a String " Dicas" está entre aspas duplas e não entre aspas simples. É interessante sempre adotar este procedimento, pois, ao utilizar variáveis, o valor da variável é utilizado durante a expressão:

      $ STR="   Dicas"
      $ expr "$STR" : '[[:blank:]]*D'
      4
      
    
  2. Como validar uma expressão?

    Qual seria o comando expr para validar uma String que pode ser resultado de uma entrada de usuário utilizando o read, uma linha de um arquivo ou saída padrão de um comando? Utilizando grep, teremos:

      $ STR="localhost.localdomain"
      $ echo "$STR" | grep -q "localhost" && echo OK
      OK
      
      if grep -q "localhost" /etc/hosts; then
       echo "Existe"
      else
       echo "Não existe"
      fi
      
    

    Estes exemplos são bem simples, o parâmetro -q do comando grep suprime a saída padrão. O Bash ainda possibilita a utiliação da forma abaixo para Pattern Matching:

      $ STR="localhost.localdomain"
      $ [[ $STR = l* ]] && echo OK
      OK
      
    
    Mas, ainda assim não será suficiente para alguns casos, como veremos a seguir. Um exemplo mais interessante seria a validação de um nome de diretório/arquivo ou uma String em relação ao padrão resultante do comando date abaixo:

      $ date '+%d%m%y-%T'
      260405-11:52:52
      
    
    O expr pode ser empregado nesta situação. Abaixo veremos que a representação [[:digit:]] equivale aos números de 0 a 9 ou [0-9]. Vejamos como seria o comando correto para validação:

      $ STR=``date '+%d%m%y-%T'``
      $ expr "$STR" : '[[:digit:]]\{6\}-[[:digit:]]\{2\}:[[:digit:]]\{2\}:[[:digit:]]\{2\}'
      15
      $ echo $?
      0
      
    
    Como vimos, o comando expr acima retorna 0, ou seja, quando há um matching ele retorna true ou verdadeiro: variável $? igual a 0. O valor de retorno pode ser armazenado em uma variável e posteriormente verificado através dos comandos abaixo:

      STR="Dicas-Linux eh legal"
      
      expr "$STR" : '.*Linux.*' > /dev/null
      
      if [ $? -eq 0 ]; then
       echo "Encontrado"
      else
       echo "Nada encontrado"
      fi
      
    
    O padrão acima corresponde a qualquer cadeia que contenha a palavra Linux. O caractere . equivale a qualquer caractere. O retorno será verdadeiro, logo, será mostrado na tela a palavra Encontrado.

    Como retornar a cadeia que está de acordo com o padrão especificado? Resposta: Utilizando parênteses. Vamos a um exemplo simples, mostrando o comando utilizado no começo deste documento:

      $ expr "   Dicas" : '\([[:blank:]]*D\)'
        D
      
    
    Este comando retorna os caracteres da String que estão de acordo com o padrão especificado através da RegEx e demarcados pelos parênteses. Note que os parênteses devem ser escapados com contra-barras para que não sejam entendidos como um caractere literal ( ou ).

  3. Como retirar da String abaixo o número de identificação do processo(PID)?

      # tail -1 /var/log/secure
      Apr 26 09:27:01 localhost sshd[2549]: error: Address already in use.
                                    
      
    
    Uma RegEx válida para esta situação seria:

      '[[:upper:]][[:alpha:]]\{2\} [[:digit:]]\{2\} [:[:digit:]]\{8\} [[:alnum:]]\{1,\} [[:alnum:]]\{1,\}\[[[:digit:]]\{1,\}\]: '
      
    
    Note que é possível especificar o número de ocorrências para cada representação(dígitos, alfa-numéricos, etc.) indicando este número entre chaves com contra-barras: \{2\}, \{1,\}. Este último quer dizer um ou mais.

    Ao executar o comando abaixo vimos que ele retorna o número de caracteres:

      $ expr "Apr 26 09:27:01 localhost sshd[2549]: error: Address already in use." : '[[:upper:]][[:alpha:]]\{2\} [[:digit:]]\{2\} [:[:digit:]]\{8\} [[:alnum:]]\{1,\} [[:alnum:]]\{1,\}\[[[:digit:]]\{1,\}\]: '
      38
      
    
    Mas, se adicionarmos os parênteses com contra-barras na sub-cadeia que desejamos obter (PID), teremos:

      $ expr "Apr 26 09:27:01 localhost sshd[2549]: error: Address already in use." : '[[:upper:]][[:alpha:]]\{2\} [[:digit:]]\{2\} [:[:digit:]]\{8\} [[:alnum:]]\{1,\} [[:alnum:]]\{1,\}\[\([[:digit:]]\{1,\}\)\]: '
      2549
      
    

Este documento teve por finalidade mostrar uma das funcionalidades do comando expr com relação a processamento de Strings. Esta ferramenta faz parte do pacote coreutils ou fileutils dependendo da distribuição Linux. Faz parte também da maioria dos sistemas operacionais UNIX (testei em AIX, HP-UX e SunOS/Solaris).

Lógico que, para tirar o máximo desta funcionalidade é necessário um bom conhecimento sobre Expressões Regulares. Para quem ainda não tem tanto conhecimento neste tópico, a melhor referência é o livro Expressões Regulares - Uma Abordagem Divertida, de autoria de Aurélio Marinho Jargas.

Adicionar comentário

* Campos obrigatórios
5000
Powered by Commentics

Comentários

Nenhum comentário ainda. Seja o primeiro!


Veja a relação completa dos artigos de Alexandre de Abreu