No diretório pai, você pode usar find
e executar grep
apenas nesses arquivos:
find . -type f -iname "file.txt" -exec grep -Hi "pattern" '{}' +
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
). Eu quero pesquisar uma string somente 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.
No diretório pai, você pode usar find
e executar grep
apenas nesses arquivos:
find . -type f -iname "file.txt" -exec grep -Hi "pattern" '{}' +
Construir comandos grep
com find
, como na resposta da Zanna , é altamente robusto, versátil e forma portátil de fazer isso (veja também resposta do sudodus ). E o muru postou uma excelente abordagem de usar grep
' --include
option . Mas se você quiser usar apenas o comando grep
e seu shell, existe outra maneira de fazê-lo - você pode fazer o o próprio shell executar a recursão necessária :
shopt -s globstar # you can skip this if you already have globstar turned on
grep -H 'pattern' **/file.txt
O sinal -H
faz com que grep
mostre o nome do arquivo, mesmo se apenas um arquivo correspondente for encontrado. Você pode passar os sinalizadores -a
, -i
e -n
(de seu exemplo) para grep
também, se necessário. Mas não passe -r
ou -R
ao usar este método. É o shell que recursa diretórios ao expandir o padrão glob contendo **
e não grep
.
Estas instruções são específicas para o shell Bash. Bash é o shell do usuário padrão no Ubuntu (e a maioria dos outros sistemas operacionais GNU / Linux), então se você estiver no Ubuntu e don não sei o que é a sua concha, é quase certamente Bash. Embora os shells populares geralmente suportem **
globs, eles nem sempre funcionam da mesma maneira. Para mais informações, consulte Stéphane Chazelas 's excelente resposta para O resultado de ls *, ls ** e ls *** em Unix.SE .
Ativando a globstar bash faz com que **
corresponda a caminhos contendo o separador de diretório ( /
). É, portanto, um glob de recursão de diretório. Especificamente, como man bash
explica:
Quando a opção globstar está ativada, e * é usado em um contexto de expansão de nome de caminho, dois * s adjacentes usados como um único padrão irá corresponder a todos os arquivos e zero ou mais diretórios e subdiretórios. Se seguido por a /, dois adjacentes * s corresponderão apenas aos diretórios e subdiretórios.
Você deve ter cuidado com isso, já que você pode executar comandos que modificam ou excluem muito mais arquivos do que você pretende, especialmente se você escreve **
quando você quis escrever *
. (É seguro neste comando, que não altera nenhum arquivo).% Co_de% desativa a opção do shell globstar.
shopt -u globstar
. find
é muito mais versátil que globstar. Qualquer coisa que você possa fazer com globstar, você pode fazer com o comando find
também. Eu gosto de globstar, e às vezes é mais conveniente, mas globstar não é uma alternativa geral para find
.
O método acima não analisa os diretórios cujos nomes começam com find
. Às vezes você não quer reciclar essas pastas, mas às vezes você faz isso.
Assim como em um glob comum, o shell cria uma lista de todos os caminhos correspondentes e os transmite como argumentos para o seu comando ( .
) no lugar do próprio glob. Se você tem tantos arquivos chamados grep
que o comando resultante seria muito longo para o sistema executar, então o método acima falhará. Na prática, você precisaria (pelo menos) milhares desses arquivos, mas isso poderia acontecer.
Os métodos que usam file.txt
não estão sujeitos a essa restrição porque:
A maneira de Zanna constrói e executa um comando find
com potencialmente muitos argumentos de caminho. Mas, se forem encontrados mais arquivos do que os que podem ser listados em um único caminho, a ação grep
-terminated +
executará o comando com alguns dos caminhos, depois executará novamente com mais alguns caminhos e assim por diante. No caso de -exec
ing para uma string em vários arquivos, isso produz o comportamento correto.
Como o método globstar abordado aqui, isso imprime todas as linhas correspondentes, com caminhos prefixados a cada um.
o caminho do sudodus executa grep
separadamente para cada grep
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, seguido por linhas correspondentes, se houver. Este é um formato de saída diferente do formato produzido pelo meu método, Zanna's e muru's .
file.txt
Um dos benefícios imediatos do uso do globstar é, por padrão, no Ubuntu, find
produzirá saída colorida. Mas você pode facilmente obter isso com grep
, também .
As contas de usuário no Ubuntu são criadas com um alias que faz com que find
realmente execute grep
(execute grep --color=auto
para ver). É uma coisa boa que os aliases são praticamente só expandiu quando você os emitiu de forma interativa , mas isso significa que se você quiser que alias grep
invoque find
com o grep
, você terá que escrevê-lo explicitamente. Por exemplo:
find . -name file.txt -exec grep --color=auto -H 'pattern' {} +
Você não precisa de find
para isso; grep
pode lidar com isso perfeitamente bem sozinho:
grep "pattern" . -airn --include="file.txt"
Em man grep
:
--exclude=GLOB
Skip files whose base name matches GLOB (using wildcard
matching). A file-name glob can use *, ?, and [...] as
wildcards, and \ to quote a wildcard or backslash character
literally.
--exclude-from=FILE
Skip files whose base name matches any of the file-name globs
read from FILE (using wildcard matching as described under
--exclude).
--exclude-dir=DIR
Exclude directories matching the pattern DIR from recursive
searches.
--include=GLOB
Search only files whose base name matches GLOB (using wildcard
matching as described under --exclude).
O método fornecido na resposta do muru , da execução de grep
com o sinalizador --include
para especificar um nome de arquivo, geralmente é a melhor escolha. No entanto, isso também pode ser feito com find
.
A abordagem nesta resposta usa find
para executar grep
separadamente para cada arquivo encontrado e imprime o caminho para cada arquivo exatamente uma vez , acima das linhas correspondentes encontradas em cada Arquivo. (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 onde você tem esses arquivos. Então corra:
find . -name "file.txt" -type f -exec echo "##### {}:" \; -exec grep -i "pattern" {} \;
Isso imprime o caminho (relativo ao diretório atual, .
e incluindo o próprio nome do arquivo) de cada arquivo denominado file.txt
, seguido por todas as linhas correspondentes no arquivo. Isso funciona porque {}
é um marcador 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 chamados file.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 que obtém de métodos que imprimem um caminho no início de cada linha correspondente.
Usar find
como este quase sempre será mais rápido do que executar grep
em todos os arquivos ( grep -arin "pattern" *
), porque find
procura os arquivos com o nome correto e ignora todos outros arquivos.
O Ubuntu usa o GNU find , que sempre expande {}
mesmo quando aparece em uma string maior , como ##### {}:
. Se você precisar de seu comando para trabalhar com find
em sistemas que talvez não suportem isso , ou você prefere usar a ação -exec
somente quando absolutamente necessário, você pode usar:
find . -name "file.txt" -type f -printf '##### %p:\n' -exec grep -i "pattern" {} \;
Para tornar a saída mais fácil de ler , você pode usar sequências de escape ANSI para obter nomes de arquivos coloridos. Isso faz com que o caminho do caminho de cada arquivo se destaque das linhas correspondentes impressas:
find . -name file.txt -printf $'\e[32m%p:\e[0m\n' -exec grep -i "pattern" {} \;
O faz com que o seu shell ative o código de escape para verde na seqüência de escape real que produz verde em um terminal, e para fazer a mesma coisa com o código de escape normal cor. Esses escapes são passados para find
, que os usa quando imprime um nome de arquivo. ( $'
'
cotação é necessária aqui porque a ação find
do -printf
não reconhece \e
para interpretar códigos de escape ANSI.)
Se preferir, você pode usar -exec
com printf
do sistema comando (que suporta \e
). Então, outra maneira de fazer o mesmo é:
find . -name file.txt -exec printf '\e[32m%s:\e[0m\n' {} \; -exec grep -i "pattern" {} \;
Apenas para indicar que, se as condições da questão puderem ser consideradas literárias, você pode usar o grep direto:
grep 'pattern' abc/def/efg/*/file.txt
ou
grep 'pattern' abc/def/efg/{1..300}/file.txt
Tags command-line grep find