Execute `grep` excluindo um arquivo em um caminho específico


12

Quero excluir o arquivo ./test/main.cppda minha pesquisa.

Aqui está o que estou vendo:

$ grep -r pattern --exclude=./test/main.cpp
./test/main.cpp:pattern
./lib/main.cpp:pattern
./src/main.cpp:pattern

Eu sei que é possível obter a saída que eu quero usando vários comandos em um arranjo de tubos e filtros, mas há algumas citações / escape que farão grepentender o que eu quero nativamente?


Uma solução baseada na filtragem da saída não é bem dimensionada porque procura desnecessariamente o arquivo antes de excluir os resultados associados. O problema é ampliado se eu quiser excluir diretórios inteiros (com --exclude-dir). É por isso que eu gostaria que o grep execute a exclusão de forma nativa.
nobar 20/05

1
Especifica --exclude não glob um caminho
Golfo Pérsico

Respostas:


6

grep Não é possível fazer isso para o arquivo em um determinado diretório, se você tiver mais arquivos com o mesmo nome em diretórios diferentes, use find:

find . -type f \! -path './test/main.cpp' -exec grep pattern {} \+


Por que você está escapando \!e \+? Parece funcionar bem sem as barras invertidas.
Nobar

@nobar Estou acostumado a isso porque alguns caracteres são palavras-chave do shell, assim você nunca ficará surpreso porque nada pode acontecer se eles escaparem.
MichalH

" grepnão pode fazer isso, use em findvez disso" - perfeito.
No20 /

4

Eu não acho que é possível com o GNU grep. Você não precisa de canos embora.

Com find:

find . ! -path ./test/main.cpp -type f -exec grep pattern {} +

Com zsh:

grep pattern ./**/*~./test/main.cpp(.)

(exclui arquivos ocultos, também para excluir .git, .svn ...).


2

Eu poderia escrever um livro: "A arte perdida de xargs". O find ... -exec … ';lança um grep para cada arquivo (mas a variante com -exec … +não). Bem, estamos perdendo ciclos de CPU hoje em dia. Por que não, certo? Mas se desempenho, memória e poder são um problema: use xargs:

find . -type f \! -path 'EXCLUDE-FILE' -print0 | xargs -r0 grep 'PATTERN'

Do GNU find's -print0vai NUL-terminate sua saída e xargs' -0honras de opção que formato como entrada. Isso garante que quaisquer caracteres engraçados que seu arquivo tenha, o pipeline não ficará confuso. A -ropção garante que não haja erro, caso findnão encontre nada.

Observe que agora você pode fazer coisas como:

find . -type f -print0 | grep -z -v "FILENAME EXCLUDE PATTERN" | 
  xargs -r0 grep 'PATTERN'

O GNU grep's -zfaz o mesmo que o xargs ' -0.


3
Algumas notas interessantes, mas não tenho certeza se você está correto sobre o problema de desempenho. Pelo que entendi, find -exec (cmd) {} +funciona da mesma forma xargse find -exec (cmd) {} \;funciona da mesma forma que xargs -n1. Em outras palavras, sua declaração só está correta se a \;versão for usada.
Nobar

3
A canalização xargsé menos eficiente do que a utilização -exec … +(embora marginalmente). Nenhuma das respostas aqui sequer menciona -exec … \;.
Gilles 'SO- stop be evil'

1
Bem, s - t. Eu namoro comigo mesmo. Obrigado pelos comentários e correções. Eu pensei que o \ + era um erro de digitação. Oh, olha, -exec ... +adicionado em janeiro de 2005. Sim, não estou desatualizado ... de jeito nenhum.
Otheus 21/05

2

Se seus findsuportes -pathforam adicionados ao POSIX em 2008, mas ainda estão ausentes no Solaris:

find . ! -path ./test/main.cpp -type f -exec grep pattern /dev/null {} +

1
Eu não acho que irá trabalhar becuase Nobar quer main.cpp em outros diretórios
Eric Renouf

1
Seu padrão também não excluirá main.cpp de todos os outros diretórios? Isso não seria desejável
Eric Renouf

@ EricRenouf: Oh, meu erro, uma leitura errada. Atualizei minha resposta.
cuonglm

@ Gilles: Por que -pathnão é POSIX?
Cuonglm

Ah, desculpe, meu erro, foi adicionado em 2008. Ainda falta o Solaris.
Gilles 'SO- stop be evil'

1

Para o registro, aqui está a abordagem que eu prefiro:

grep pattern $(find . -type f ! -path './test/main.cpp')

Mantendo o grepcomando no início do comando, acho que isso é um pouco mais claro - além de não desabilitar grepo realce de cores. Em certo sentido, usar finduma substituição de comando é apenas uma maneira de estender / substituir o subconjunto (limitado) de pesquisa de arquivos da grepfuncionalidade de.


Para mim, a find -execsintaxe é meio misteriosa. Uma complexidade find -execé a (às vezes) necessidade de escapar vários caracteres (principalmente se \;for usado no Bash). Apenas para fins de colocar as coisas em contextos familiares, os dois comandos a seguir são basicamente equivalentes:

find . ! -path ./test/main.cpp -type f -exec grep pattern {} +
find . ! -path ./test/main.cpp -type f -print0 |xargs -0 grep pattern

Se você deseja excluir subdiretórios , pode ser necessário usar um curinga. Eu não entendo completamente o esquema aqui - fale sobre arcano :

grep pattern $(find . -type f ! -path './test/main.cpp' ! -path './lib/*' )

Uma observação adicional para generalizar findsoluções baseadas em uso em scripts : A greplinha de comando deve incluir a opção -H/ --with-filename. Caso contrário, ele alterará a formatação da saída sob a circunstância de que exista apenas um nome de arquivo nos resultados da pesquisa find. Isso é notável porque não parece ser necessário se você estiver usando grepa pesquisa de arquivos nativa (com a -ropção).

... Melhor ainda, é incluir /dev/nullcomo primeiro arquivo a pesquisar. Isso resolve dois problemas:

  • Ele garante que, se houver um arquivo para pesquisar, greppensará que existem dois e use o modo de saída de vários arquivos.
  • Ele garante que, se não houver arquivos para pesquisar, greppensará que existe um arquivo e não ficará esperando no stdin.

Portanto, a resposta final é:

grep pattern /dev/null $(find . -type f ! -path './test/main.cpp')

Você não deve usar a saída de findem uma substituição de comando. Isso é interrompido se houver nomes de arquivos contendo espaços ou outros caracteres especiais. Use find -exec, é robusto e fácil de usar.
Gilles 'SO- stop be evil'

@ Gilles: Ponto muito bom - também a saída pode exceder os limites de tamanho da linha de comando de alguns programas. Advertência emptor.
Nobar

Ugh. A sintaxe 'find' é terrivelmente difícil. '-o' é um operador "ou" (também '-ou' no Linux), mas seu uso típico (por exemplo, com '-prune') não é mapeado conceitualmente para a noção de um or lógico ou. É um funcional ou melhor que lógico ou.
Nobar

Outra forma de excluir subdiretórios com base na correspondência um nome: find -iname "*target*" -or -name 'exclude' -prune. Bem, isso meio que funciona - o diretório removido será listado, mas não será pesquisado. Se você não quer que ele na lista, você pode acrescentar uma espécie de redundância! -name 'exclude'
Nobar
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.