grep: Quais padrões * são correspondidos, não qual texto?

3

Ao manter nossas regras de verificação de log local, seria bom saber quais não estão mais sendo usadas. Estou tentando encontrar uma maneira razoavelmente eficiente de fazer isso.

Em outras palavras, dado um número bastante grande de padrões de expressão regular estendida ( grep -E ) do GNU grep (por volta de 700), e muita saída de syslog (dezenas se não centenas de milhões de linhas), eu gostaria de determine quais desses ≈700 padrões correspondem a nenhum das linhas do syslog. Melhor ainda seria descobrir quantas linhas de syslog cada padrão corresponde.

A maneira óbvia é executar grep -c -E "$pattern" «massive-logfile» ≈700 vezes, uma vez por padrão. Mas isso parece ineficiente, especialmente se o arquivo de log massivo não couber na RAM.

Existe alguma maneira eficiente de fazer isso?

    
por derobert 10.08.2017 / 18:02

4 respostas

3

Você pode usar awk cujos regexps são muito semelhantes a grep -E :

awk '!patterns_read{patterns[$0]; next}
     {for (p in patterns) if ($0 ~ p) c[p]++}
     END {
       for (p in patterns) printf "'%s' was matched %d times\n", p, c[p]
     }' patterns patterns_read=1 log files
    
por 10.08.2017 / 18:20
1

Para cada padrão:

if ! grep -q "$pattern" /path/to/input; then
    echo "/${pattern}/ not found."
fi

Este é o AFAIK, na verdade, a única maneira de fazer isso, porque se você pesquisar vários padrões de uma só vez, saberá apenas que você correspondeu pelo menos um deles. Você poderia tentar, se você tem um monte de ORs lógicos (por exemplo, /(needle|pin)/ , começando apenas por repetir cada uma das daquelas permutações, mas você ainda terá que testar uma expressão por vez.

    
por 10.08.2017 / 18:08
1

Dependendo da complexidade das REs, você pode usar uma linguagem como Python (não testada):

#! /usr/bin/env python3
import re, sys
res = ["re1", "re2", ... ]  # or read from a file
recs = [re.compile(r) for r in res]
matches = {}
for line in sys.stdin:
    for r in recs:
        if r.match(line):
            matches[r] += 1
for r in matches:
    if matches[r] == 0:
        print(r.pattern)

É claro que, se as expressões regulares contiverem strings que podem não ser tomadas literalmente pelo suporte de regex mais avançado do Python, isso não funcionará. Você provavelmente poderia eliminar muito trabalho alimentando a saída de grep com isso (desde então, eliminamos as linhas nas quais nenhuma regex corresponde).

    
por 10.08.2017 / 18:17
1
perl -lne '
   # read in the patterns into a hash
   @ARGV and $h{$_}=s|/|\/|gr,next;

   # delete pattern if matched, so we wont have to
   # expend efforts on it for the subsequent lines
   while (my($pat) = each %h) {
      delete $h{$pat} if /$h{$pat}/;
   }

   # what remains are those that did not match
   END {
      print "These patterns did not match:";
      print for keys %h;
   }
' patterns_file log_file
    
por 10.08.2017 / 19:21