Como excluir alguns arquivos que não correspondem a determinadas extensões com grep?

8

Quero exibir todas as linhas que contêm a palavra OKrecursivamente de um diretório. Mas há algumas extensões que preciso excluir do resultado:

*~
*.map
*.js except *.debug.js

Eu tentei:

grep -r --exclude={*~,*.map} "OK" /some/dir

Só que não sei como remover do resultado todos os .jsarquivos que não são de depuração .

Pergunta Overflow
fonte

Respostas:

7

Eu passaria isso por um segundo greppara removê-los:

grep -r --exclude={\*~,\*.map} "OK" bar/ | grep -vP '(?<!debug)\.js'

O -vinverte o jogo, imprimindo linhas que não correspondem ao padrão e -Ppermite Perl Compatible Regular Expressions que nos permitem usar visões traseiras negativos . Esse regex em particular corresponderá ao .jsque não é precedido por debugquais meios (desde que estamos invertendo as correspondências) que somente esses .jsarquivos serão impressos.

No entanto, como o @QuestionOverflow apontou nos comentários, isso pode ter o efeito colateral não intencional de filtrar as linhas que contêm OKe, jsuma vez que grep -vé aplicada a toda a saída, não apenas ao nome do arquivo. Para evitar isso, basta adicionar dois pontos (que é o que é grepusado para separar nomes de arquivos do conteúdo):

grep -r --exclude={*~,*.map} "OK" bar/ | grep -vP '(?<!debug).js:'

Isso ainda falhará se sua linha de entrada contiver foo.js:ou se o nome do arquivo contiver :. Portanto, para ter certeza, use uma abordagem diferente:

grep -Tr --exclude={*~,*.map} "OK" bar/ | grep -vP '(?<!debug).js\t'

As -Tcausas greppara imprimir uma guia entre o nome do arquivo e o conteúdo do arquivo. Portanto, se simplesmente adicionarmos um \tno final da regex, ela corresponderá apenas aos nomes dos arquivos, e não ao conteúdo da linha.

Ainda assim, o usofind pode fazer mais sentido, independentemente.

terdon
fonte
1
Eu excluiria inadvertidamente linhas nos arquivos que eu quero, mas contendo ambas OKe .jsna mesma linha?
Pergunta Overflow
@QuestionOverflow ah, sim, de fato, boa captura. Veja a resposta atualizada.
terdon
Resposta fantástica. Tem que aceitar o seu, uma vez que peço especificamente grep. Obrigado.
Pergunta Overflow
@QuestionOverflow, você é muito bem-vindo. Em geral, porém, findé provavelmente melhor para esse tipo de coisa. Conseguir o direito greppode ser complicado, como você apontou :).
terdon
Suas soluções falhar se a pessoa tem a failglobopção definida no shell: bash: no match: --exclude=*~ Você precisa citar seus argumentos padrão glob para --excludeescondê-los de expansão shell, por exemplo--exclude={\*~,\*.map}
Ian D. Allen
7

Eu usaria findpara localizar os arquivos e canalizar o resultado através de xargs:

$ find . -type f \! -name "*~" \
                 \! -name "*.map" \
                 \! \( -name "*.js" -and \! -name "*.debug.js" \) \
         -print0 | xargs -0 grep "OK"

Ele procura todos os arquivos que não correspondem " *~", " *.map" ou " *.jsmas não *.debug.js".

Com findo uso, você pode procurar facilmente regras bastante complexas e essa abordagem evita que você acidentalmente remova falsos positivos, como poderia acontecer com o dobro grep.

Andreas Wiese
fonte
Resposta agradável também :)
Pergunta Overflow
3
Sim, esta é provavelmente a melhor maneira, +1. Você também pode usar em -exec grep OK {} +vez de xargsevitar um programa extra.
terdon
2
@IDAllen não, observe que eu sugeri que -exec +não -exec \;, que executará o menor número possível de comandos, como os demais xargs.
terdon
4

Com zshvocê pode fazer:

setopt extendedglob
grep OK some/dir/**/^(*~|*.map|(^*debug).js)

Desde que a lista de argumentos não seja muito longa, nesse caso, você sempre pode:

printf '%s\0' some/dir/**/^(*~|*.map|(^*debug).js) | xargs -0 grep OK
Graeme
fonte
Além disso, você pode fazer o último zshautoload zargszargs some/dir/**/^(*~|*.map|(^*debug).js) -- grep OK
-only
2

Se você não se importa de ver a saída ligeiramente fora de ordem (se quiser, pode classificá-la):

grep -r --exclude={*~,*.map,*.js} "OK" /some/dir **/*.debug.js

Isso requer que o seu shell suporte **globbing recursivo: o zsh sai da caixa, o bash o faz depois da execução shopt -s globstar, o ksh93 o faz depois da execução set -o globstar.

Sem **suporte no shell, você pode usar dois comandos grep:

grep -r --exclude={*~,*.map,*.js} "OK" /some/dir
grep -r --include=*.debug.js "OK" /some/dir
Gilles 'SO- parar de ser mau'
fonte
Meu shell suporta **, mas parece haver algo errado com o argumento extra **/*.debug.js, fazendo com que o grep interprete OKcomo um diretório. Você já tentou executá-lo?
Pergunta Overflow
@QuestionOverflow Meu erro, eu troquei a ordem dos argumentos.
Gilles 'SO- stop be evil'
2

Você pode usar ripgrep. Por padrão, ele ignora arquivos ocultos e respeita seu .gitignorearquivo.

Você pode especificar as regras de inclusão ou exclusão usando os seguintes parâmetros:

-g/ --glob GLOBInclua ou exclua arquivos e diretórios de pesquisa que correspondem ao glob especificado.

-t/ --type TYPEPesquise apenas arquivos correspondentes a TYPE. Podem ser fornecidos sinalizadores de vários tipos.

-T/ --type-not TYPENão pesquise arquivos correspondentes a TYPE.

Use a --type-listbandeira para listar todos os tipos disponíveis.

Aqui estão alguns exemplos simples:

rg -Tjs "OK"                              # Excludes *.js, *.jsx, *.vue files.
rg -tpy "OK"                              # Includes Python files.
rg --type-add 'map:*.map' -tmap PATTERN   # Excludes *.map files.
rg -g '!*.js' -g '*.debug.js' PATTERN     # Excludes *.js apart of *.debug.js.

Aqui é a solução completa para excluir *.~, *.map, *.js, mas não *.debug.js:

rg -g '*.*' -g '!*.~' -g '!*.map' -g '!*.js' -g '*.debug.js' "OK"

Teste:

$ touch file.~ file.map file.js file.debug.js file.txt file.md
$ rg --files
file.debug.js
file.js
file.map
file.md
file.txt
$ rg -g '*.*' -g '!*.~' -g '!*.map' -g '!*.js' -g '*.debug.js' --files
file.debug.js
file.md
file.txt
kenorb
fonte