Eu tenho um diretório (por exemplo, abc/def/efg
) com muitos subdiretórios (por exemplo ::) abc/def/efg/(1..300)
. Todos esses subdiretórios têm um arquivo comum (por exemplo, file.txt
). Quero pesquisar uma string apenas neste, file.txt
excluindo outros arquivos. Como posso fazer isso?
Eu usei grep -arin "pattern" *
, mas é muito lento se tivermos muitos subdiretórios e arquivos.
command-line
grep
find
Rajesh Keladimath
fonte
fonte
Respostas:
No diretório pai, você pode usar
find
e executargrep
apenas esses arquivos:fonte
-H
paragrep
que, nos casos em que apenas um caminho seja passado, esse caminho ainda seja impresso (em vez de apenas as linhas correspondentes do arquivo).Você também pode usar a globstar.
Construir
grep
comandos comfind
, como na resposta de Zanna , é uma maneira altamente robusta, versátil e portátil de fazer isso (consulte também a resposta de sudodus ). E muru publicou uma excelente abordagem ao usargrep
a--include
opção . Mas se você deseja usar apenas ogrep
comando e seu shell, existe outra maneira de fazer isso - você pode fazer com que o próprio shell execute a recursão necessária :A
-H
bandeira fazgrep
mostrar o nome do arquivo, mesmo que apenas um arquivo correspondente seja encontrado. Você pode passar o-a
,-i
e-n
bandeiras (a partir do seu exemplo) paragrep
, assim, se é isso que você precisa. Mas não passe-r
ou-R
ao usar este método. É o shell que repete os diretórios na expansão do padrão global que contém**
, e nãogrep
.Essas instruções são específicas para o shell Bash. O Bash é o shell de usuário padrão no Ubuntu (e na maioria dos outros sistemas operacionais GNU / Linux); portanto, se você está no Ubuntu e não sabe qual é o seu shell, é quase certamente o Bash. Embora os shells populares geralmente suportem
**
globs que atravessam o diretório , eles nem sempre funcionam da mesma maneira. Para mais informações, consulte Stéphane Chazelas 's excelente resposta a O resultado de ls *, ls ** e ls *** em Unix.SE .Como funciona
Ligar o globstar festa opção shell faz
**
caminhos jogo contendo o separador de diretório (/
). É, portanto, um globo recorrente no diretório. Especificamente, comoman bash
explica:Você deve ter cuidado com isso, pois é possível executar comandos que modificam ou excluem muito mais arquivos do que você pretende, especialmente se você escrever
**
quando pretender*
. (É seguro neste comando, que não altera nenhum arquivo.)shopt -u globstar
Desativa a opção shell do globstar.Existem algumas diferenças práticas entre globstar e
find
.find
é muito mais versátil que a globstar. Tudo o que você pode fazer com a globstar, também pode fazer com ofind
comando. Eu gosto da globstar, e às vezes é mais conveniente, mas a globstar não é uma alternativa geralfind
.O método acima não procura nos diretórios cujos nomes começam com a
.
. Às vezes você não deseja recursar essas pastas, mas às vezes deseja.Como em um globo comum, o shell cria uma lista de todos os caminhos correspondentes e os passa como argumentos para o seu comando (
grep
) no lugar do próprio globo. Se você tiver tantos arquivos chamadosfile.txt
que o comando resultante seria muito longo para a execução do sistema, o método acima falhará. Na prática, você precisaria (pelo menos) de milhares desses arquivos, mas isso poderia acontecer.Os métodos utilizados
find
não estão sujeitos a esta restrição, porque:O jeito de Zanna cria e executa um
grep
comando com potencialmente muitos argumentos de caminho. Porém, se forem encontrados mais arquivos do que os que podem ser listados em um único caminho, a ação+
terminada-exec
executará o comando com alguns dos caminhos, em seguida, executará novamente com mais alguns caminhos e assim por diante. No caso degrep
ing para uma cadeia de caracteres em vários arquivos, isso produz o comportamento correto.Como o método globstar abordado aqui, ele imprime todas as linhas correspondentes, com caminhos anexados a cada um.
O caminho de sudodus é executado
grep
separadamente para cada umfile.txt
encontrado. Se houver muitos arquivos, pode ser mais lento que alguns outros métodos, mas funciona.Esse método localiza arquivos e imprime seus caminhos, seguidos por linhas correspondentes, se houver. Este é um formato de saída diferente do formato produzido pelo meu método, o de Zanna e o de Muru .
Obtendo cores com
find
Um dos benefícios imediatos do uso da globstar é, por padrão no Ubuntu,
grep
produzir uma saída colorida. Mas você pode facilmente obter isso comfind
, também .As contas de usuário no Ubuntu são criadas com um alias que
grep
realmente funcionagrep --color=auto
(executealias grep
para ver). É bom que os aliases sejam expandidos apenas quando você os emitir de maneira interativa , mas isso significa que, se você quiserfind
chamargrep
com o--color
sinalizador, precisará escrevê-lo explicitamente. Por exemplo:fonte
bash
shell para que isso funcione. Você não dizê-lo implicitamente "a opção shell globstar bash" mas pode ser facilmente perdida por pessoas que lêem muito rapidamente.**
globs que atravessam o diretório , sua crítica principal está correta: a apresentação**
desta resposta é específica do bash, com shopt sendo apenas bash e o termo "globstar" sendo (eu acho) bash e Apenas tcsh. Eu havia ignorado isso originalmente por causa dessas complexidades, mas você está certo que é um pouco confuso. Em vez de discuti-lo detalhadamente nesta resposta, vinculei-me a outro post (bastante completo) que faz o trabalho pesado.-e
que não deve ser aplicado aos caminhos, mas isso é facilmente corrigido. Para o primeiro comando, apenas omita-e
. Para o segundo, usefind . -name file.txt -printf $'\e[32m%p:\e[0m\n' -exec grep -i "pattern" {} \;
oufind . -name file.txt -exec printf '\e[32m%s:\e[0m\n' {} \; -exec grep -i "pattern" {} \;
. Às vezes, os usuários preferem o seu caminho (com-e
uso fixo) aos outros, que imprimem um caminho por linha correspondente ; o seu imprime um caminho por arquivo encontrado, seguido pelosgrep
resultados.grep
si só, não fará o que você está fazendo. Algumas outras críticas também estavam erradas.grep -H
executado por-exec
não será colorido sem--color
(ouGREP_COLOR
). IEEE 1.003,1-2.008 não garante{}
expande em##### {}:
, mas Ubuntu tem GNU encontrar, o que faz . Se estiver tudo bem com você , editarei sua postagem para corrigir o-e
erro (e esclarecer seu caso de uso) e você poderá ver se deseja cancelar a exclusão. (Eu tenho o representante para ver / editar mensagens eliminadas.)Você não precisa
find
disso;grep
pode lidar com isso perfeitamente bem por conta própria:De
man grep
:fonte
find?
O método dado na resposta do muru , de executar
grep
com o--include
sinalizador para especificar um nome de arquivo, geralmente é a melhor opção. No entanto, isso também pode ser feito comfind
.A abordagem nesta resposta é
find
executadagrep
separadamente para cada arquivo encontrado e imprime o caminho de cada arquivo exatamente uma vez , acima das linhas correspondentes encontradas em cada arquivo. (Os métodos que imprimem o caminho na frente de cada linha correspondente são abordados em outras respostas.)Você pode alterar o diretório para o topo da árvore de diretórios em que você possui esses arquivos. Então corra:
Isso imprime o caminho (relativo ao diretório atual
.
, e incluindo o próprio nome do arquivo) de cada arquivo nomeadofile.txt
, seguido por todas as linhas correspondentes no arquivo. Isso funciona porque{}
é um espaço reservado para o arquivo encontrado. O caminho de cada arquivo é separado de seu conteúdo, sendo prefixado com#####
e é impresso apenas uma vez, antes das linhas correspondentes desse arquivo. (Os arquivos chamadosfile.txt
que não contêm correspondências ainda têm seus caminhos impressos.) Você pode achar essa saída menos confusa do que a obtida com métodos que imprimem um caminho no início de cada linha correspondente.Usar
find
dessa maneira quase sempre será mais rápido do que executargrep
em todos os arquivos (grep -arin "pattern" *
), porquefind
procura os arquivos com o nome correto e ignora todos os outros arquivos.O Ubuntu usa o GNU find , que sempre se expande
{}
mesmo quando aparece em uma string maior , como##### {}:
. Se você precisar que seu comando trabalhefind
em sistemas que talvez não suportem isso , ou se prefere usar a-exec
ação somente quando for absolutamente necessário, poderá usar:Para facilitar a leitura da saída , você pode usar seqüências de escape ANSI para obter nomes de arquivos coloridos. Isso faz com que o cabeçalho de cada arquivo se destaque melhor das linhas correspondentes impressas abaixo dele:
Isso faz com que o seu shell transforme o código de escape para verde na sequência de escape real que produz verde em um terminal e faça o mesmo com o código de escape para cores normais. Essas fugas são passadas para
find
, que as usam quando imprime um nome de arquivo. (a$'
'
cotação é necessária aqui porquefind
a-printf
ação de não reconhece\e
para interpretar os códigos de escape ANSI.)Se preferir, você pode usar
-exec
com oprintf
comando do sistema (que suporta\e
). Então, outra maneira de fazer a mesma coisa é:fonte
find abc/def/efg -name "file.txt" -type f -exec echo -e "##### {}:" \; -exec grep -i "pattern" {} \;
cd abc/def/efg
comando 'mudança diretório' :-)-e
opçãoecho
? Isso fará com que ele altere os nomes de arquivos que contêm barras invertidas. (2) Não é garantido que o uso{}
como parte de um argumento funcione. Seria melhor dizer-exec echo "#####" {} \;
ou-exec printf "##### %s:\n" {} \;
. (3) Por que não usar-print
ou apenas-printf
? (4) Considere tambémgrep -H
.find . -name "file.txt" -type f -exec echo -e "\0033[32m{}:\0033[0m" \; -exec grep -i "pattern" {} \;
2) Você pode estar certo, mas até agora isso está funcionando para mim. 3) -print e -printf também são alternativas. 4) Isso já está lá na resposta principal. - De qualquer forma, você é bem-vindo com a sua própria resposta :-)-exec
ligações. Basta usargrep -H
e isso imprimirá o nome do arquivo (em cores) e o texto correspondente.Apenas para apontar que, se as condições da pergunta puderem ser consideradas literárias, você poderá usar o grep direto:
ou
fonte