O que se expande para todos os arquivos no diretório atual recursivamente?
91
Eu sei que se **/*.extexpande para todos os arquivos em todos os subdiretórios correspondentes *.ext, mas o que é uma expansão semelhante que inclui todos esses arquivos no diretório atual também?
Minha festa não aguenta **/*.ext. Tem certeza de que funciona para você?
tangens
@tangens Você deve habilitar a globstaropção de acordo com a resposta de Dennis.
Kenorb
Respostas:
110
Isso funcionará no Bash 4:
ls -l {,**/}*.ext
Para que o glob de asterisco duplo funcione, a globstaropção precisa ser definida (padrão: ativado):
shopt -s globstar
De man bash:
globstar
Se definido, o padrão ** usado em uma expansão de nome de arquivo con‐
o texto corresponderá a arquivos e zero ou mais diretórios e
subdiretórios. Se o padrão for seguido por um /, apenas
diretórios e subdiretórios correspondem.
Agora estou me perguntando se pode ter havido um bug no processamento globstar, porque agora usando simplesmente ls **/*.extestou obtendo resultados corretos.
Independentemente disso, olhei para a análise que kenorb fez usando o repositório VLC e encontrei alguns problemas com essa análise e em minha resposta imediatamente acima:
As comparações com a saída do findcomando são inválidas, pois a especificação -type fnão inclui outros tipos de arquivo (diretórios em particular) e os lscomandos listados provavelmente incluem . Além disso, um dos comandos listados,ls -1 {,**/}*.* - que parece ser baseado no meu acima, só exibe nomes que incluem um ponto para os arquivos que estão em subdiretórios. A pergunta do OP e minha resposta incluem um ponto, pois o que se busca são arquivos com extensão específica.
O mais importante, entretanto, é que há um problema especial ao usar o lscomando com o padrão globstar **. Muitas duplicatas surgem, pois o padrão é expandido pelo Bash para todos os nomes de arquivo (e nomes de diretório) na árvore que está sendo examinada. Após a expansão, o lscomando lista cada deles e seu conteúdo, se forem diretórios.
Exemplo:
Em nosso diretório atual está o subdiretório Ae seu conteúdo:
A
└── AB
└── ABC
├── ABC1
├── ABC2
└── ABCD
└── ABCD1
Nessa árvore, **expande para "AA / AB A / AB / ABC A / AB / ABC / ABC1 A / AB / ABC / ABC2 A / AB / ABC / ABCD A / AB / ABC / ABCD / ABCD1" (7 entradas) . Se você fizer echo **isso, será a saída exata que obterá e cada entrada será representada uma vez. No entanto , se você fizer ls **isso, irá gerar uma lista de cada uma dessas entradas. Basicamente, ele é ls Aseguido por ls A/ABetc., então A/ABé mostrado duas vezes. Além disso, lsseparará a saída de cada subdiretório:
Portanto, o uso de wc -lconta todas aquelas linhas em branco e cabeçalhos de seção de nome de diretório, o que torna a contagem ainda mais difícil.
Este é outro motivo pelo qual você não deve analisarls .
Como resultado desta análise adicional, recomendo não usar o padrão globstar em qualquer circunstância que não seja iterar sobre uma árvore de arquivos desta maneira:
for entry in**do
something "$entry"done
Como comparação final, usei um repositório de origem Bash que tinha à mão e fiz o seguinte:
Eu costumava trmudar os espaços para novas linhas, o que só é válido aqui, já que nenhum nome inclui espaços. Eu costumava sedremover a liderança ./de cada linha de saída de find. Classifiquei a saída de, findpois normalmente ela não está classificada e a expansão de globs do Bash já está classificada. Como você pode ver, a única saída de difffoi a .saída do diretório atual por find. Quando o fiz, ls ** | wc -la saída tinha quase o dobro de linhas.
A melhor resposta! mas acho que **/*.extdeve ser o suficiente. Além disso, você não terá os arquivos ocultos a menos que você shopt -s dotglob.
gniourf_gniourf
2
Para desativar globstar: shopt -u globstar.
Kenorb
4
@gniourf_gniourf A questão na verdade pede para incluir o diretório atual especificamente para que não, **/*.extnão seja suficiente
msciwoj
2
@dotnetCarpenter: A versão do Bash que vem com o MacOS é 3.2, que não oferece suporte ao globstar, como você descobriu. Um asterisco duplo é tratado da mesma forma que um único. Globstar foi introduzido no Bash 4.0.
Pausado até novo aviso.
13
Isso imprimirá todos os arquivos no diretório atual e seus subdiretórios que terminam em '.ext'.
Embora essa resposta não atenda à "expansão" solicitada pelo OP no sentido mais estrito, é mais provável que produza o resultado desejado.
Pausado até novo aviso.
7
Você pode usar: **/*.*para incluir todos os arquivos recursivamente (habilitado por:) shopt -s globstar.
Veja abaixo o teste de outras variações e como elas se comportam.
Pasta de teste com 3472 arquivos na pasta de repositório VLC de amostra :
(Total de arquivos de 3472 contou como por: find . -type f | wc -l)
ls -1 **/*.* - retorna 3338
ls -1 {,**/}*.*- retorna 3341 (conforme proposto por Dennis )
ls -1 {,**/}* - retorna 8265
ls -1 **/*- retorna 7817, exceto arquivos ocultos (conforme proposto por Dennis )
ls -1 **/{.[^.],}*- retorna 7869 (conforme proposto por Dennis )
ls -1 {,**/}.?* - retorna 15855
ls -1 {,**/}.* - retorna 20321
Então eu acho que o método mais próximo de listar todos os arquivos recursivamente é o primeiro exemplo ( **/*.*) de acordo com o comentário gniourf-gniourf (assumindo que os arquivos tenham as extensões adequadas, ou use uma específica), já que o segundo exemplo dá mais algumas duplicatas como abaixo :
Para incluir arquivos ocultos, use: shopt -s dotglob(desativar por shopt -u dotglob). Não é recomendado, pois pode afetar comandos como mvou rme você pode remover acidentalmente os arquivos errados.
No terminal Mac e bash com globstar habilitado, achei a solução acima ( **/*.*) informativa e funcionou melhor. A resposta aceita causou duplicatas de itens no diretório superior. Meu padrão de trabalho era:"${path}"**/*.*
mummybot de
Seria interessante tentar isso com outras opções como nullglob e dotglob
Wilf
3
$ find .-type f
Isso listará todos os arquivos no diretório atual. Você pode então fazer algum outro comando na saída usando -exec
$find .-type f -exec grep "foo"{} \;
Isso fará o grep de cada arquivo do find para a string "foo".
Agora que já se passaram 11 anos, pode ser a hora de alguém apontar que find . -type fse aplica recursivamente com a raiz no diretório atual, não apenas para o diretório atual.
Roger Dahl
3
Por que não apenas usar a expansão de chaves para incluir o diretório atual também?
./{*,**/*}.ext
A expansão do Brace ocorre antes da expansão do glob, então você pode efetivamente fazer o que quiser com as versões mais antigas do bash e pode deixar de usar o globstar nas versões mais novas.
Além disso, é considerada uma boa prática em bash incluir o líder ./em seus padrões glob.
**/*.ext
. Tem certeza de que funciona para você?globstar
opção de acordo com a resposta de Dennis.Respostas:
Isso funcionará no Bash 4:
Para que o glob de asterisco duplo funcione, a
globstar
opção precisa ser definida (padrão: ativado):De
man bash
:Agora estou me perguntando se pode ter havido um bug no processamento globstar, porque agora usando simplesmente
ls **/*.ext
estou obtendo resultados corretos.Independentemente disso, olhei para a análise que kenorb fez usando o repositório VLC e encontrei alguns problemas com essa análise e em minha resposta imediatamente acima:
As comparações com a saída do
find
comando são inválidas, pois a especificação-type f
não inclui outros tipos de arquivo (diretórios em particular) e osls
comandos listados provavelmente incluem . Além disso, um dos comandos listados,ls -1 {,**/}*.*
- que parece ser baseado no meu acima, só exibe nomes que incluem um ponto para os arquivos que estão em subdiretórios. A pergunta do OP e minha resposta incluem um ponto, pois o que se busca são arquivos com extensão específica.O mais importante, entretanto, é que há um problema especial ao usar o
ls
comando com o padrão globstar**
. Muitas duplicatas surgem, pois o padrão é expandido pelo Bash para todos os nomes de arquivo (e nomes de diretório) na árvore que está sendo examinada. Após a expansão, ols
comando lista cada deles e seu conteúdo, se forem diretórios.Exemplo:
Em nosso diretório atual está o subdiretório
A
e seu conteúdo:Nessa árvore,
**
expande para "AA / AB A / AB / ABC A / AB / ABC / ABC1 A / AB / ABC / ABC2 A / AB / ABC / ABCD A / AB / ABC / ABCD / ABCD1" (7 entradas) . Se você fizerecho **
isso, será a saída exata que obterá e cada entrada será representada uma vez. No entanto , se você fizerls **
isso, irá gerar uma lista de cada uma dessas entradas. Basicamente, ele éls A
seguido porls A/AB
etc., entãoA/AB
é mostrado duas vezes. Além disso,ls
separará a saída de cada subdiretório:Portanto, o uso de
wc -l
conta todas aquelas linhas em branco e cabeçalhos de seção de nome de diretório, o que torna a contagem ainda mais difícil.Este é outro motivo pelo qual você não deve analisar
ls
.Como resultado desta análise adicional, recomendo não usar o padrão globstar em qualquer circunstância que não seja iterar sobre uma árvore de arquivos desta maneira:
Como comparação final, usei um repositório de origem Bash que tinha à mão e fiz o seguinte:
Eu costumava
tr
mudar os espaços para novas linhas, o que só é válido aqui, já que nenhum nome inclui espaços. Eu costumavased
remover a liderança./
de cada linha de saída defind
. Classifiquei a saída de,find
pois normalmente ela não está classificada e a expansão de globs do Bash já está classificada. Como você pode ver, a única saída dediff
foi a.
saída do diretório atual porfind
. Quando o fiz,ls ** | wc -l
a saída tinha quase o dobro de linhas.fonte
globstar
é padrãooff
**/*.ext
deve ser o suficiente. Além disso, você não terá os arquivos ocultos a menos que vocêshopt -s dotglob
.globstar
:shopt -u globstar
.**/*.ext
não seja suficienteIsso imprimirá todos os arquivos no diretório atual e seus subdiretórios que terminam em '.ext'.
fonte
Você pode usar:
**/*.*
para incluir todos os arquivos recursivamente (habilitado por:)shopt -s globstar
.Veja abaixo o teste de outras variações e como elas se comportam.
Pasta de teste com 3472 arquivos na pasta de repositório VLC de amostra :
(Total de arquivos de 3472 contou como por:
find . -type f | wc -l
)ls -1 **/*.*
- retorna 3338ls -1 {,**/}*.*
- retorna 3341 (conforme proposto por Dennis )ls -1 {,**/}*
- retorna 8265ls -1 **/*
- retorna 7817, exceto arquivos ocultos (conforme proposto por Dennis )ls -1 **/{.[^.],}*
- retorna 7869 (conforme proposto por Dennis )ls -1 {,**/}.?*
- retorna 15855ls -1 {,**/}.*
- retorna 20321Então eu acho que o método mais próximo de listar todos os arquivos recursivamente é o primeiro exemplo (
**/*.*
) de acordo com o comentário gniourf-gniourf (assumindo que os arquivos tenham as extensões adequadas, ou use uma específica), já que o segundo exemplo dá mais algumas duplicatas como abaixo :e o outro gera ainda mais duplicatas.
Para incluir arquivos ocultos, use:
shopt -s dotglob
(desativar porshopt -u dotglob
). Não é recomendado, pois pode afetar comandos comomv
ourm
e você pode remover acidentalmente os arquivos errados.fonte
**/*.*
) informativa e funcionou melhor. A resposta aceita causou duplicatas de itens no diretório superior. Meu padrão de trabalho era:"${path}"**/*.*
Isso listará todos os arquivos no diretório atual. Você pode então fazer algum outro comando na saída usando -exec
Isso fará o grep de cada arquivo do find para a string "foo".
fonte
find . -type f
se aplica recursivamente com a raiz no diretório atual, não apenas para o diretório atual.Por que não apenas usar a expansão de chaves para incluir o diretório atual também?
A expansão do Brace ocorre antes da expansão do glob, então você pode efetivamente fazer o que quiser com as versões mais antigas do bash e pode deixar de usar o globstar nas versões mais novas.
Além disso, é considerada uma boa prática em bash incluir o líder
./
em seus padrões glob.fonte