grep para encontrar instâncias de "Foo" em que "Bar" não aparece em 10 linhas


10

Suponha que eu queira pesquisar em uma árvore inteira todos os arquivos CPP em que "Foo" ocorre. Eu devo fazer:

find . -name "*.cpp" | xargs grep "Foo"

Agora, suponha que eu queira listar apenas as instâncias em que alguma outra string, como "Bar", não ocorre dentro de três linhas do resultado anterior.

Então, com dois arquivos:

a.cpp

1 Foo
2 qwerty
3 qwerty

b.cpp

1 Foo
2 Bar
3 qwerty

Gostaria de construir uma pesquisa simples onde "Foo" de a.cpp é encontrado, mas "Foo" de b.cpp não é.

Existe uma maneira de fazer isso de uma maneira bastante simples?


Talvez a solução possa estar na opção grep -A e / ou grep -B e / ou grep -C. Eu estou tentando, mas sem sucesso ....
maurelio79 14/01

@ maurelio79: Minha teoria atual é essa. Grep para "Foo" usando -A 10 para contexto. Canalize isso para grep -v Bar. Canalize para sed para obter o nome do arquivo e o número da linha. Canalize isso para (alguma coisa?) Para imprimir essa linha.
precisa saber é o seguinte

Respostas:


17

Com pcregrep:

pcregrep --include='\.cpp$' -rnM 'Foo(?!(?:.*\n){0,2}.*Bar)' .

A chave está na -Mopção que é exclusiva pcregrepe usada para corresponder a várias linhas ( pcregrepextrai mais dados do arquivo de entrada conforme necessário ao caminhar pelo ER).

(?!...)é o operador RE antecipado do perl / PCRE. Foo(?!...)corresponde Foodesde ...que não corresponda ao que se segue.

...sendo (?:.*\n){0,2}.*Bar( .não correspondendo a um caractere de nova linha), que é de 0 a 2 linhas, seguido por uma linha que contém Bar.


+1: excelente. Muito obrigado; Tenho certeza de que não foi fácil descobrir a regex correta. Eu aprecio muito seus esforços. Isso parece estar funcionando exatamente como eu queria.
precisa saber é o seguinte

2
Pergunta secundária se você gostaria de responder. Como você conheceu pcregrep? Eu nunca ouvi falar disso antes.
John Dibling

@JohnDibling, eu pessoalmente descobri recentemente em unix.SE . Esse ER não é particularmente complexo, especialmente quando você está familiarizado com o operador de ER (?!...)antecipado perl.
Stéphane Chazelas 14/01

9

Não importa, basta usar pcregrepcomo sugerido por @StephaneChazelas.


Isso deve funcionar:

$ find . -name "*.cpp" | 
    while IFS= read -r file; do 
      grep -A 3 Foo "$file" | grep -q Bar || echo "$file"; 
    done 

A idéia é usar o -Aswitch grep para gerar as linhas correspondentes e as N linhas seguintes. Em seguida, você passa o resultado por um grep Bare, se isso não corresponder (saída> 0), você repetirá o nome do arquivo.

Se você sabe que possui nomes de arquivos sãos (sem espaços, novas linhas ou outros caracteres estranhos), pode simplificar:

$ for file in $(find . -name "*.cpp"); do 
   grep -A 3 Foo "$file" | grep -q Bar || echo "$file"; 
  done 

Por exemplo:

terdon@oregano foo $ cat a.cpp 
1 Foo
2 qwerty
3 qwerty
terdon@oregano foo $ cat b.cpp 
1 Foo
2 Bar
3 qwerty
terdon@oregano foo $ cat c.cpp 
1 Foo
2 qwerty
3 qwerty
4 qwerty
5. Bar
terdon@oregano foo $ for file in $(find . -name "*.cpp"); do grep -A 3 Foo "$file" | grep -q Bar || echo "$file"; done 
./c.cpp
./a.cpp

Observe que c.cppé retornado apesar de conter Barporque a linha com Baré mais de 3 linhas depois Foo. Você pode controlar o número de linhas que deseja pesquisar, alterando o valor passado para -A:

$ for file in $(find . -name "*.cpp"); do 
   grep -A 10 Foo "$file" | grep -q Bar || echo "$file"; 
  done 
./a.cpp

Aqui está um mais curto (supondo que você use bash):

$ shopt -s globstar 
$ for file in **/*cpp; do 
    grep -A 10 Foo "$file" | grep -q Bar || echo "$file"; 
  done

IMPORTANTE

Como Stephane Chazelas apontou nos comentários, as soluções acima também imprimirão arquivos que não contêm Foonada. Este evita que:

for file in **/*cpp; do 
  grep -qm 1 Foo "$file" && 
  (grep -A 3 Foo "$file" | grep -q Bar || echo "$file"); 
done

+1 puro. Um pouco mais complexo do que eu esperava, mas nada mal.
John Dibling

Isso pressupõe que "Foo" ocorra apenas uma vez. Isso também reportará os arquivos que não contêm Foo. Você tem cotações ausentes.
Stéphane Chazelas

@StephaneChazelas obrigado, citações corrigidas. Você está certo ao relatar arquivos sem Fooe eu consertei isso, mas não entendo seu ponto de vista sobre várias instâncias de Foo. Deve lidar com eles corretamente.
terdon

@JohnDibling ver atualizações.
terdon

1
Não reportaria um arquivo contendo 100 linhas de "Foo" seguido de "Bar".
Stéphane Chazelas

0

Não testado, estou no meu telefone:

find . -name "*.cpp" | xargs awk '/foo/{t=$0;c=10}/bar/{c=0;t=""}c{c--}t&&!c{print t;t=""}END&&t{print t}' 

algo parecido.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.