O que é um comando do Linux para localizar arquivos contendo algumas cadeias de caracteres, mas não outra?

4

Eu estou tentando pesquisar um monte de arquivos XML para determinadas seqüências de caracteres, mas não outras seqüências de caracteres e estou tendo problemas para colocar um comando juntos para fazê-lo. Só quero listar os nomes dos arquivos que correspondem aos critérios de inclusão / exclusão. Eu estive tentando:

find . -name *.xml -exec grep -li "string1\|string2" {} \; | xargs grep -Li "string3\|string4"

Mas estou tendo problemas porque os nomes de arquivos retornados de encontrar tem espaços em seus nomes e o segundo grep divide todos em pequenos pedaços e, claro, não encontra tais arquivos. Eu tentei adicionar -0 ao xargs , e remove os erros, mas diz "Nome do arquivo longo demais" e só executa o primeiro grep.

Como faço para ajustar esse comando para que ele funcione corretamente em arquivos com espaços nos nomes?

Cicerone Cojocaru
fonte
Isso não pertence ao super usuário?
2
O script do bash é relacionado à programação e à manutenção do sistema, portanto, eu diria que ele pertence a qualquer um dos sites.
aioobe
1
Não em uma caixa unix no momento, mas eu acho que (assumindo o GNU grep), você poderia tentar algo como find . -name "*.xml" -exec grep -liZ "string1\|string2" {} \; | xargs -0 grep -Li "string3\|string4" (O Z bandeira para grep produz terminadores zero em sua lista de resultados, que deve ser compatível com xargs -0 )
Matt Gibson
@pilcrow Isso não funcionará muito nesta circunstância, porque o -print0 estará imprimindo apenas os nomes dos arquivos, não selecionando apenas os nomes de arquivos correspondidos pelo primeiro grep. É a saída do primeiro grep que está passando pelo pipe, e é por isso que acho que o sinalizador -Z é o que é necessário. Difícil dizer sem uma caixa para brincar, embora :)
Matt Gibson
@Matt, sim, e há mais de uma maneira de fazer isso. :)
pilcrow

Respostas:

3

Há mais de uma maneira de fazer isso. Isso deve manter o número total de processos executados no mínimo:

find . -name \*.xml -print0          \   # List of *.xml files (NUL-terminated)
  | xargs -0 grep -Zli 'string[12]'  \   # is input to first grep, which sends (NUL-term'd)
  | xargs -0 grep -Li  'string[34]'      # file list to second grep

obrigado Matt Gibson por nos lembrar da -Z flag para o GNU grep.

pilcrow
fonte
Sim, esse é um método legal. @Cicerone - também note que @pilcrow e eu estamos assumindo que você precisa escapar do seu *.xml de alguma forma, para evitar a expansão do shell.
Matt Gibson
Este também funciona e é extremamente rápido!
3

Você pode fazer tudo em um find que evita todos os problemas com espaços em nomes de arquivos. Algo como

find . -exec grep -liq "string1\|string2" {} \; -not -exec grep -liq "string3\|string4" {} \; -print

O "-q" suprime toda a saída do grep. o -exec primary retorna true quando o processo sai com status 0, como o grep faz quando encontra uma correspondência, e o -not primária inverte isso. Então, colocamos diretamente duas condições no finde, como resultado, imprimimos apenas os nomes de arquivos que satisfazem ambos - não é necessário tubo!

Ernest Friedman-Hill
fonte
Eu estava apenas trabalhando nos detalhes disso quando sua resposta apareceu.
Desculpe @Colin. Eu odeio quando isso acontece comigo :)
Ernest Friedman-Hill
+1 para uma solução em que é altamente provável que o arquivo ainda esteja no cache de disco quando o segundo grep for trabalhar.
Ole Tange
2

Como mencionado no meu comentário, acho que tudo o que você está perdendo é a bandeira -Z no primeiro grep para ir junto com o -0 que você estava tentando em seu xargs:

find . -name "*.xml" -exec grep -liZ "string1\|string2" {} \; | xargs -0 grep -Li "string3\|string4"
Matt Gibson
fonte
0

Se o fator limitante for CPU (ou seja, seu disco é rápido) e você tiver mais núcleos de CPU, você pode usar o GNU Parallel:

find . -type f| parallel grep -Lq foo {} '||' grep -l bar {}

Ao executar os dois grep s imediatamente após cada outra chance, os arquivos ainda estão no cache de disco. Se o disco procura são lentos, você pode adicionar -j1 para desabilitar o paralelismo.

Assista ao vídeo de introdução para saber mais: http://www.youtube.com/watch?v=OpaiGYxkSuQ

Ole Tange
fonte