grep recursivo para palavras em um tipo de arquivo específico

7

Eu queria um comando de linha de comando para pesquisar todos os scripts de shell no sistema de arquivos em busca de uma palavra específica, então perguntei no trabalho e obtive as seguintes soluções:

grep word 'find / -name \*.sh 2>/dev/null'
find / -name "*.sh" 2>/dev/null | xargs grep word

No entanto, não estou familiarizado com a linha de comando, então ambas as soluções parecem opacas para mim. Eu prefiro fazer algo que pareça:

ls -r *.sh | cat | grep -H word

Mas parece que você não pode canalizar nomes de arquivos para o gato (pelo menos eu acho que é esse o problema).

Qual é a solução mais legível? E em segundo lugar, qual é a solução mais eficiente?

Editar: eu precisava saber em qual arquivo a palavra estava localizada, para poder modificar o script.

    
por paulrehkugler 26.04.2012 / 20:26

3 respostas

8

Editar: Se você tiver utilitários GNU, veja a resposta de Gilles para um método usando habilidades de recursão do GNU grep que é muito mais simples que a abordagem find . Se você quiser apenas exibir nomes de arquivos, você ainda desejará adicionar a opção -l conforme descrito abaixo.

Use grep -l word para imprimir apenas os nomes dos arquivos que contêm uma correspondência.

Se você quiser encontrar todos os arquivos no sistema de arquivos que terminam em .sh , começando na raiz / , então find é a ferramenta mais apropriada.

A recomendação mais portátil e eficiente é:

find / -type f -name '*.sh' -exec grep -l word {} + 2>/dev/null

Isso é tão legível quanto possível e não é difícil de analisar se você entender a semântica por trás de cada um dos componentes.

  • find / : executar find iniciando na raiz do sistema de arquivos, /
  • -type f : apenas corresponde a arquivos regulares
  • -name '*.sh' : ... e apenas correspondem aos arquivos cujos nomes terminam em .sh
  • -exec ... {} + : comando de execução especificado em ... em arquivos correspondentes em grupos, em que {} é substituído pelos nomes de arquivos no grupo. A idéia é executar o comando em tantos arquivos quanto possível dentro dos limites do sistema ( ARG_MAX ). A eficiência da forma {} + vem da minimização do número de vezes que o comando ... deve ser chamado, maximizando o número de arquivos passados para cada chamada de ... .
  • grep -l word {} : onde o {} é o mesmo {} repetido acima e é substituído por nomes de arquivos. Como explicado anteriormente, grep -l imprime os nomes dos arquivos que contêm uma correspondência para word .
  • 2>/dev/null : oculta as mensagens de erro (tecnicamente, redirecione o erro padrão para o buraco negro que é /dev/null ). Isso ocorre por motivos estéticos e práticos, pois a execução de find em / provavelmente resultará em resmas de mensagens de "permissão negada" às quais você pode não se importar para arquivos que você não tem permissão para ler e diretórios que você não tem permissão para atravessar.

Existem alguns problemas com as sugestões recebidas e publicadas na sua pergunta. Ambos

grep word 'find / -name \*.sh 2>/dev/null

e

find / -name "*.sh" 2>/dev/null | xargs grep word

falha em arquivos com espaços em branco em seus nomes. É melhor evitar colocar nomes de arquivos na substituição do comando. O primeiro tem o problema adicional de possivelmente entrar no limite de ARG_MAX. O segundo é próximo do que eu sugiro, mas não há uma boa razão para usar xargs aqui, sem mencionar que o uso seguro e correto de xargs requer o sacrifício da portabilidade para algumas opções somente GNU ( find -print0 | xargs -0 ).

    
por 26.04.2012 / 22:51
8

Em sistemas Linux, Cygwin ou outros, não embarcados, com GNU grep , em FreeBSD , em NetBSD e OSX :

grep -r --include='*.sh' word .

Não analise a saída de ls . E não use a substituição de comando na saída de find , como jw013 explicou .

    
por 27.04.2012 / 02:18
2

A combinação de grep e find é, em muitos casos, ack ( betterthangrep.com ):

ack [OPTION]... PATTERN [FILE]

Para o seu exemplo, considere usar

ack --shell word /

Notas

ack

  • procura (por padrão) recursivamente, mas
  • ignora diretórios (por padrão) de sistemas de controle de versão comuns, por exemplo, .git , .hg , .svn , ...
  • pode restringir facilmente seus resultados usando filtros para tipos de arquivos comuns (veja abaixo os padrões de nome de arquivo distintos)
  • tem uma sintaxe semelhante a grep e os mesmos argumentos / semelhantes como -i para "ignorar maiúsculas e minúsculas", etc.
  • pode ser chamado de ack-grep no seu sistema (em distros baseadas no Debian, se bem me lembro)

Padrões de nome de arquivo

A opção --shell é a abreviação de --type=shell e inclui vários tipos de arquivo: atualmente .sh .bash .csh .tcsh .ksh .zsh de acordo com

ack --help-types

Se você quiser apenas .sh arquivos, você deve definir (adicionar) seu próprio tipo sh e usar este filtro ( --sh ) como este:

ack word --type-add=sh=.sh --sh /

Isso parece um pouco complicado, mas permite a pesquisa recursiva de .sh arquivos abaixo de / . Para uma pesquisa local (sem especificar o diretório inicial, por exemplo, \ ), seria mais fácil:

ack word *.sh
    
por 27.04.2012 / 09:10