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?

Ramon
fonte
4
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:

...
<blank line>
directory name:
content-item
content-item

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:

shopt -s globstar dotglob
diff <(echo ** | tr ' ' '\n') <(find . | sed 's|\./||' | sort)
0a1
> .

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.

Pausado até novo aviso.
fonte
5
Eu testei Ubuntu e Cygwin, e globstaré padrãooff
Steven Penny
12
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'.

find . -name '*.ext' -print
unutbu
fonte
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 :

$ diff -u <(ls -1 {,**/}*.*) <(ls -1 **/*.*)
--- /dev/fd/63  2015-04-19 15:25:07.000000000 +0100
+++ /dev/fd/62  2015-04-19 15:25:07.000000000 +0100
@@ -1,6 +1,4 @@
 COPYING.LIB
-COPYING.LIB
-Makefile.am
 Makefile.am
@@ -45,7 +43,6 @@
 compat/tdestroy.c
 compat/vasprintf.c
 configure.ac
-configure.ac

e o outro gera ainda mais duplicatas.


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.

Kenorb
fonte
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".

Amir Afghani
fonte
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.

clone206
fonte