grep para "termo" e excluir "outro termo"

23

Eu estou tentando construir uma pesquisa grep que procura por um termo, mas exclui as linhas que têm um segundo termo. Eu queria usar vários -e "pattern" opções, mas isso não funcionou.

Aqui está um exemplo de um comando que eu tentei e a mensagem de erro que ele gerou.

grep -i -E "search term" -ev "exclude term"
grep: exclude term: No such file or directory

Parece-me que o -v aplica-se a todos os termos / padrões de pesquisa. Como isso é executado, mas não inclui search term nos resultados.

grep -i -E "search term" -ve "exclude term"
nelaaro
fonte
Existe alguma outra opção para excluir, como às vezes temos que grep linhas em torno de uma palavra e se excluir na próxima operação usando '|' , apenas remove essa palavra, mas não remove o bloco dessa palavra
Learner

Respostas:

37

Para e expressões com grep você precisa de duas invocações:

grep -Ei "search term" | grep -Eiv "exclude term"

Se os termos que você está procurando não forem expressões regulares, use a correspondência de cadeia fixa ( -F ) O que é mais rápido:

grep -F "search term" | grep -Fv "exclude term"
Thor
fonte
12

De repente, invocando grep duas vezes, só há uma maneira de pensar nisso. Envolve Expressões regulares compatíveis com Perl (PCRE) e alguns bastante hacky asserções de olhar ao redor .

Procurar por foo excluindo correspondências que contenham Barra , você pode usar:

grep -P '(?=^((?!bar).)*$)foo'

Veja como isso funciona:

  • (?!bar) corresponde a qualquer coisa que não Barra sem consumir caracteres da string. Então . consome um único caractere.

  • ^((?!bar).)* repete o acima do início da string ( ^ ) até ao final do mesmo ( $ ). Ele falhará se bar é encontrado em qualquer ponto dado, desde (?!bar) não vai corresponder.

  • (?=^((?!bar).)*$) garante que a string corresponda ao padrão anterior, sem consumir caracteres da string.

  • foo procura por foo como sempre.

Eu encontrei este hack em Expressão regular para corresponder a string que não contém uma palavra? . Em Bart Kiers 'resposta , você pode encontrar uma explicação muito mais detalhada de como o look-ahead negativo opera.

Dennis
fonte
8

Se você quiser fazer isso em um passo, você pode usar o awk ao invés do grep.

Formato:

echo "some text" | awk '/pattern to match/ && !/pattern to exclude/'

Exemplos:

  • echo "hello there" | awk '/hello/ && !/there/'

Não retorna nada.

  • echo "hello thre" | awk '/hello/ && !/there/'

Retorna: olá lá

  • echo "hllo there" | awk '/hello/ && !/there/'

Não retorna nada.

Para vários padrões, você pode usar parênteses para agrupá-los.

Exemplos:

  • echo "hello thre" | awk '(/hello/ || /hi/) && !/there/'

Retorna: olá lá

  • echo "hi thre" | awk '(/hello/ || /hi/) && !/there/'

Retorna: oi ai

  • echo "hello there" | awk '(/hello/ || /hi/) && !/there/'

Não retorna nada.

  • echo "hi there" | awk '(/hello/ || /hi/) && !/there/'

Não retorna nada.

Philip Reese
fonte
1
Funcionou para mim, mas perdi as cores = P
Leopoldo Sanczyk
1
Cores de qual saída? Se você estiver tentando preservar as cores com ls, use o argumento "--color = always" sempre que analisar a saída (ou você sempre perderá as cores ao analisar o texto). Exemplo: ls --color=always | awk '/hello/ && !/goodbye/'
Philip Reese
Obrigado pela resposta @Philip! Eu tentei isso antes, mas sem sucesso. Eu acho que como o padrão tem o texto colorido, ele não combina mais tarde, e eu deveria incluir algum tipo de código de cor no padrão. De qualquer forma, o seu é o caminho mais rápido que eu encontrei para fazer grep -R em vários arquivos de código usando a linha de comando do Ubuntu.
Leopoldo Sanczyk
1

De minhas experiências, não parece fazer muita diferença se você canaliza seus termos de exclusão através grep ou sed. Sed tem alguns outros recursos úteis de substituição de texto que eu costumo usar para filtrar melhor a saída de arquivos de log. Então eu vou usar o sed enquanto eu combino um grande número de filtros no sed.

 wc /var/log/tomcat/tomcat.2013-01-14.log.1
  1851725

 / usr / bin / time grep -i -E "(loginmanager)" /var/log/tomcat/tomcat.2013-01-14.log.1 | sed -e "/ login OK / d" -e "/ Login expirado / d" | banheiro
24.05usuário 0.15sistema 0: 25.27solvido 95% da CPU (0avgtext + 0avgdata 3504maxresident) k
0inputs + 0outputs (0maior + 246minor) pagefaults 0swaps
   5614 91168 1186298

 / usr / bin / time grep -i -E "(loginmanager)" /var/log/tomcat/tomcat.2013-01-14.log.1 | sed -e "/ login OK / d" -e "/ Login expirado / d" | banheiro
23.50usuário 0.16sistema 0: 24.48susado 96% da CPU (0avgtext + 0avgdata 3504maxresident) k
0inputs + 0outputs (0maior + 246minor) pagefaults 0swaps
   5614 91168 1186298

 / usr / bin / time grep -i -E "(loginmanager)" /var/log/tomcat/tomcat.2013-01-14.log.1 | grep -v -e "login OK" -e "Login expirado" | banheiro
23.08usuário 0.14sistema 0: 23.55substituído 98% da CPU (0avgtext + 0avgdata 3504maxresident) k
0inputs + 0outputs (0maior + 246minor) pagefaults 0swaps
   5614 91168 1186298

 / usr / bin / time grep -i -E "(loginmanager)" /var/log/tomcat/tomcat.2013-01-14.log.1 | grep -v -e "login OK" -e "Login expirado" | banheiro
23.50usuário 0.15sistema 0: 25.27susado 93% da CPU (0avgtext + 0avgdata 3488maxresident) k
0inputs + 0outputs (0maior + 245minor) pagefaults 0swaps
   5614 91168 1186298 
nelaaro
fonte
3
Tente comparar o tempo de execução de grep -F ao invés de grep -E e não use -i se você não precisar.
Thor
Mas então você não fornece exemplos usando sed ;)
Benjamin R