Como localizo arquivos que não contêm um determinado padrão de sequência?


Respostas:


818

Se o seu grep tiver a opção -L(ou --files-without-match):

$ grep -L "foo" *

1
Como apontado em outro lugar, o ack ajuda a evitar arquivos .svn (subversão) por padrão.
GuruM

11
@GuruM Isso pode ser feito em GNU grep, exportando a variável GREP_OPTIONS='--exclude-dir=.svn --exclude-dir=.git': ^)
bufh

6
Ou o equivalente usando ag :ag -L 'foo'
bishop

5
Funciona como mágica! Dica: use em -rLvez de -Lpara coincidir com subdiretórios
Ufos 18/10

1
@ Larry - Uma maneira mais limpa de evitar problemas ocultos é usar a opção longa "vazia" como esta: grep -L 'foo' -- *O padrão é que os comandos que usam opções longas são usadas --para indicar que não há mais opções após esse ponto.
Paddy Landau

45

Dê uma olhada ack. Ele faz a .svnexclusão automaticamente, fornece expressões regulares do Perl e é um download simples de um único programa do Perl.

O equivalente ao que você está procurando deve ser ack:

ack -L foo

24

Você pode fazer isso apenas com grep (sem encontrar).

grep -riL "foo" .

Esta é a explicação dos parâmetros usados ​​no grep

     -L, --files-without-match
             each file processed.
     -R, -r, --recursive
             Recursively search subdirectories listed.

     -i, --ignore-case
             Perform case insensitive matching.

Se você usar l(em minúsculas), obterá o oposto (arquivos com correspondências)

     -l, --files-with-matches
             Only the names of files containing selected lines are written

17

O comando a seguir fornece todos os arquivos que não contêm o padrão foo:

find .  -not  -ipath '.*svn*' -exec  grep  -H -E -o -c  "foo"  {} \; | grep 0

4
Você deseja alterar o grep 0 no final para grep 0 $ (caso contrário, você obtém correspondências incorretas nos arquivos que têm o caractere 0 no nome do arquivo).
clouseau 23/12/2009

9
O @clouseau tem quase toda a razão ... No entanto, grep '0$'também corresponderia arquivos com múltiplos de 10 linhas! Você precisa grep ':0$'no final verificar se há um ': 0' explícito no final da linha. Então você receberá apenas arquivos com zero linhas correspondentes.
TrinitronX

O UNIX em que eu estou não possui versões do find ou grep com essas opções, então tive que seguir o comando "ack" sugerido em outros comentários.
KC Baltz

14

O comando a seguir exclui a necessidade de a localização filtrar as svnpastas usando um segundo grep.

grep -rL "foo" ./* | grep -v "\.svn"

9

Você realmente precisará de:

find .  -not  -ipath '.*svn*' -exec  grep  -H -E -o -c  "foo"  {} \; | grep :0\$

6

Eu tive boa sorte com

grep -H -E -o -c "foo" */*/*.ext | grep ext:0

Minhas tentativas com grep -vapenas me deram todas as falas sem "foo".


4

Problema

Eu preciso refatorar um projeto grande que usa .phtmlarquivos para escrever HTML usando código PHP embutido. Quero usar modelos de bigode . Quero encontrar quaisquer .phtmlgiles que não contenham a string, new Mustachepois eles ainda precisam ser reescritos.

Solução

find . -iname '*.phtml' -exec grep -H -E -o -c 'new Mustache' {} \; | grep :0$ | sed 's/..$//'

Explicação

Antes dos tubos:

Encontrar

find . Encontre arquivos recursivamente, iniciando neste diretório

-iname '*.phtml'O nome do arquivo deve conter .phtml( iisso não diferencia maiúsculas de minúsculas)

-exec 'grep -H -E -o -c 'new Mustache' {}'Execute o grepcomando em cada um dos caminhos correspondentes

Grep

-H Sempre imprima cabeçalhos de nome de arquivo com linhas de saída.

-E Interprete o padrão como uma expressão regular estendida (ou seja, force o grep a se comportar como egrep).

-o Imprime apenas a parte correspondente das linhas.

-c Somente uma contagem de linhas selecionadas é gravada na saída padrão.


Isso fornecerá uma lista de todos os caminhos de arquivo que terminam em .phtml, com uma contagem do número de vezes que a string new Mustacheocorre em cada um deles.

$> find . -iname '*.phtml$' -exec 'grep -H -E -o -c 'new Mustache' {}'\;

./app/MyApp/Customer/View/Account/quickcodemanagestore.phtml:0
./app/MyApp/Customer/View/Account/studio.phtml:0
./app/MyApp/Customer/View/Account/orders.phtml:1
./app/MyApp/Customer/View/Account/banking.phtml:1
./app/MyApp/Customer/View/Account/applycomplete.phtml:1
./app/MyApp/Customer/View/Account/catalogue.phtml:1
./app/MyApp/Customer/View/Account/classadd.phtml:0
./app/MyApp/Customer/View/Account/orders-trade.phtml:0

O primeiro canal grep :0$filtra esta lista para incluir apenas linhas que terminam em :0:

$> find . -iname '*.phtml' -exec grep -H -E -o -c 'new Mustache' {} \; | grep :0$

./app/MyApp/Customer/View/Account/quickcodemanagestore.phtml:0
./app/MyApp/Customer/View/Account/studio.phtml:0
./app/MyApp/Customer/View/Account/classadd.phtml:0
./app/MyApp/Customer/View/Account/orders-trade.phtml:0

O segundo sed 's/..$//'canal retira os dois caracteres finais de cada linha, deixando apenas os caminhos do arquivo.

$> find . -iname '*.phtml' -exec grep -H -E -o -c 'new Mustache' {} \; | grep :0$ | sed 's/..$//'

./app/MyApp/Customer/View/Account/quickcodemanagestore.phtml
./app/MyApp/Customer/View/Account/studio.phtml
./app/MyApp/Customer/View/Account/classadd.phtml
./app/MyApp/Customer/View/Account/orders-trade.phtml

3

Se você estiver usando o git, ele pesquisará todos os arquivos rastreados:

git grep -L "foo"

e você pode procurar em um subconjunto de arquivos rastreados se você tiver ** o globbing do subdiretório ativado ( shopt -s globstarem .bashrc, consulte o seguinte ):

git grep -L "foo" -- **/*.cpp

1

Meu grep não possui nenhuma opção -L. Eu acho uma solução alternativa para conseguir isso.

As idéias são:

  1. despejar todo o nome do arquivo que contém a sequência merecida em um txt1.txt.
  2. despejar todo o nome do arquivo no diretório para um txt2.txt.
  3. faça a diferença entre o arquivo 2 dump com o comando diff.

    grep 'foo' *.log | cut -c1-14 | uniq > txt1.txt
    grep * *.log | cut -c1-14 | uniq > txt2.txt
    diff txt1.txt txt2.txt | grep ">"
    

Eu esqueço os comandos, mas em vez de despejar nomes de arquivos, você pode realmente fazer um diffentre dois fluxos de saída (acho que você envolve os comandos entre parênteses, e há um colchete angular em algum lugar também), se o seu sistema suportar, o que eu acho é a questão, uma vez que não suportagrep -L
Dexygen

1

find *20161109* -mtime -2|grep -vwE "(TRIGGER)"

Você pode especificar o filtro em "localizar" e a cadeia de exclusão em "grep -vwE". Use mtime em find se precisar filtrar também o horário modificado.


Isso parece me mostrar todas as linhas sem a string, o OP pede apenas os nomes dos arquivos.
Ben Farmer

1

Abrir relatório de erro

Conforme comentado por @tukan, há um relatório de bug aberto para a Ag referente ao sinalizador -L/ --files-without-matches:

Como há pouco progresso no relatório de erros, a -Lopção mencionada abaixo não deve ser considerada , desde que o erro não tenha sido resolvido. Use abordagens diferentes apresentadas neste tópico. Citando um comentário para o relatório de erros [grifo meu]:

Alguma atualização sobre isso? -Lignora completamente as correspondências na primeira linha do arquivo. Parece que, se isso não for corrigido em breve, a bandeira deve ser removida completamente, pois efetivamente não funciona como anunciado .


O Silver Searcher - Ag (função pretendida - consulte o relatório de erros)

Como uma alternativa poderosa grep, você pode usar o The Silver Searcher - Ag :

Uma ferramenta de busca de código semelhante ao ack, com foco na velocidade.

Olhando man ag, encontramos a opção -Lou --files-without-matches:

...

OPTIONS
    ...

    -L --files-without-matches
           Only print the names of files that don´t contain matches.

Ou seja, para pesquisar recursivamente os arquivos que não correspondem foo, no diretório atual:

ag -L foo

Para procurar apenas arquivos atuais no diretório atualfoo , basta especificar --depth=0a recursão:

ag -L foo --depth 0

Isso falha de tempos em tempos devido ao -Lerro - github.com/ggreer/the_silver_searcher/issues/238
tukan

@ tukan obrigado pelo aviso. Eu atualizei a resposta; escolhendo não excluir a resposta, mas abrindo com as informações sobre o bug.
DFRI

1

outra alternativa quando grep não possui a opção -L (IBM AIX por exemplo), com nada além de grep e o shell:

for file in * ; do grep -q 'my_pattern' $file || echo $file ; done

-4
grep -irnw "filepath" -ve "pattern"

ou

grep -ve "pattern" < file

O comando acima nos dará o resultado conforme -v encontra o inverso do padrão que está sendo pesquisado


1
Isso imprime as linhas que não contêm o padrão. Você pode adicionar a -lopção para imprimir apenas o nome do arquivo; mas isso ainda imprime os nomes de qualquer arquivo que contenha qualquer linha que não contenha o padrão. Acredito que o OP deseja encontrar os arquivos que não contêm nenhuma linha que contenha o padrão.
tripleee

O comando que você forneceu lista os arquivos em "caminho de arquivo" com todas as suas linhas que não contêm "padrão".
Aprodan 30/05

-6

O comando a seguir pode ajudá-lo a filtrar as linhas que incluem a substring "foo".

cat file | grep -v "foo"

2
Isso imprime linhas que não correspondem, não nomes de arquivos que não contêm correspondência em nenhuma linha. Para adicionar insulto à lesão, é um uso inútil decat .
tripleee
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.