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

536

Como localizo os arquivos no diretório atual que não contêm a palavra foo(usando grep)?

Senthil Kumar
fonte

Respostas:

818

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

$ grep -L "foo" *
ghostdog74
fonte
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
Andy Lester
fonte
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
Adrian
fonte
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
Senthil Kumar
fonte
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"
user999305
fonte
9

Você realmente precisará de:

find .  -not  -ipath '.*svn*' -exec  grep  -H -E -o -c  "foo"  {} \; | grep :0\$
Forrest Tiffany
fonte
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".

Johnny
fonte
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
Sujo
fonte
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
Zak
fonte
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 ">"
    
user6305682
fonte
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.

zandeep
fonte
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
dfri
fonte
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
JMD
fonte
-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

Jay
fonte
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"
walkerlin
fonte
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