quarta-feira, 21 de setembro de 2022

Regexp com Lookaround no Grep

Muitas vezes encontrei-me concatenando greps para progressivamente encontrar dados e remover as partes redundantes. Algo assim:


  grep -Po "\(\d+\)" arquivo.log | grep -Po "\d+"
  

O primeiro grep extrai números cercados por parênteses e o segundo extrai os parênteses e deixa apenas os dígitos.

Para evitar essa repetição, podemos lançar mão dos operadores de lookaround (lookahead e lookbehind).


  grep -Po "(?<=\()\d+(?=\))" arquivo.log
  

O primeiro, um lookbehind, verifica se a expressão inicia com um parêntese "(?<=\()". O segundo, um lookahead, verifica se a expressão termina com um parêntese "(?=\))".

Esses operadores não consomem o que encontram, então o resultado não aparece na saída do grep. Isso explica também por que o lookbehind precisa ser diferente do lookahead; ele entra em ação quando a expressão regular já passou da posição dele.

Uma funcionalidade interessante seria permitir ao grep produzir uma expressão arbitrária com base nos grupos da expressão regular:


  grep -Po "\((\d+)\)" -out "$1" arquivo.log
  

Equanto isso não acontece, eu posso usar os operadores de lookaround para extrair valores e depois calcular a média usando awk, como no exemplo abaixo.


   grep -Po "(?<=\()\d+(?=\))" arquivo.log \
     | awk '{ sum += $1 } END { if (NR > 0) print sum / NR }'
     

Podemos chamar isso de CLI/BI.