Listar subdiretórios apenas n em profundidade

58

O Festival armazena dados do pacote de voz na seguinte estrutura de diretório de exemplo:

/usr/share/festival/voices/<language>/<voicepack name>

Qual é a linha simples mais simples (de preferência usando ls) para imprimir apenas os <voicepack name>, em todos os <language>subdiretórios potencialmente numerosos ?

user66001
fonte

Respostas:

80

Estou no Fedora e esses pacotes de voz estão em um local um pouco diferente:

$ ls /usr/share/festival/lib/voices/*/ -1 | grep -vE "/usr|^$"
kal_diphone
ked_diphone
nitech_us_awb_arctic_hts
nitech_us_bdl_arctic_hts
nitech_us_clb_arctic_hts
nitech_us_jmk_arctic_hts
nitech_us_rms_arctic_hts
nitech_us_slt_arctic_hts

Você pode modificar isso assim:

$ ls /usr/share/festival/voices/*/ -1 | grep -vE "/usr|^$"

Usando find

O uso lsdessa mansão geralmente é desaprovado, porque lsé difícil analisar a saída de . Melhor usar o findcomando, assim:

$ find /usr/share/festival/lib/voices -maxdepth 2 -mindepth 2 \
    -type d -exec basename {} \;
nitech_us_awb_arctic_hts
nitech_us_bdl_arctic_hts
nitech_us_slt_arctic_hts
nitech_us_jmk_arctic_hts
nitech_us_clb_arctic_hts
nitech_us_rms_arctic_hts
ked_diphone
kal_diphone

Detalhes da localização e nome da base

Este comando funciona produzindo uma lista de caminhos completos para arquivos com exatamente 2 níveis de profundidade em relação a este diretório:

/usr/share/festival/lib/voices

Essa lista é assim:

$ find /usr/share/festival/lib/voices -maxdepth 2 -mindepth 2 
/usr/share/festival/lib/voices/us/nitech_us_awb_arctic_hts
/usr/share/festival/lib/voices/us/nitech_us_bdl_arctic_hts
/usr/share/festival/lib/voices/us/nitech_us_slt_arctic_hts
/usr/share/festival/lib/voices/us/nitech_us_jmk_arctic_hts
/usr/share/festival/lib/voices/us/nitech_us_clb_arctic_hts
/usr/share/festival/lib/voices/us/nitech_us_rms_arctic_hts
/usr/share/festival/lib/voices/english/ked_diphone
/usr/share/festival/lib/voices/english/kal_diphon

Mas queremos a última parte desses diretórios, o nó da folha. Para que possamos basenameanalisá-lo:

$ basename /usr/share/festival/lib/voices/us/nitech_us_awb_arctic_hts
nitech_us_awb_arctic_hts

Juntando tudo, podemos fazer com que o findcomando passe cada diretório profundo de 2 níveis para o basenamecomando. A notação basename {}é o que está fazendo essas conversões de nome de base. Encontre chama através do -execswitch.

slm
fonte
lol, praticamente exatamente a mesma resposta, grandes mentes e tudo o que :).
terdon
+1 - Para aqueles que se atrapalham ao descobrir o que -exec basename {}faz, você poderia explicar aqui?
user66001
@ user66001 - deixe-me saber se isso explica o suficiente.
Slm
@ user66001 - você pode aceitar uma das respostas se ele resolve o seu problema para o seu satisfication 8-)
SLM
11
O comando find é o que eu preciso 99% do tempo. Limitar max e min foi fundamental - eu fiz max. Exemplo: find ~/ -maxdepth 1 -mindepth 1 -type d | xargs du -csh | sort -h Encontra os maiores diretórios ordenados em tamanho
oligofren
23

O mais fácil é

ls -d /usr/share/festival/voices/*/*

Isso é expandido pelo shell para todos os subdiretórios /usr/share/festival/voices/e depois para o conteúdo de cada um desses subdiretórios.

Se você deseja apenas descer para um nível específico, como o título sugere, com algumas implementações findcomo GNU e BSD:

find /usr/share/festival/voices/ -mindepth 2 -maxdepth 3 -type d

Isso encontrará todos os diretórios ( -type d) que estão em um subdiretório por /usr/share/festival/voices/causa, mindepth 2mas não estão mais profundos que 3 níveis abaixo ( maxdepth 3). De man find:

   -maxdepth levels
          Descend at most levels (a non-negative integer) levels of direc
          tories below the command line arguments.  -maxdepth 0
           means only apply the tests and  actions  to  the  command  line
          arguments.

   -mindepth levels
          Do  not apply any tests or actions at levels less than levels (a
          non-negative integer).  -mindepth  1  means  process  all  files
          except the command line arguments.
terdon
fonte
Sim, é como olhar no espelho 8-)
slm
+1 Como vocês dois obtiveram 2 votos é interessante. A votação cruzada explica 1 cada;) PS: Eu queria nomes de diretório, então, basta mudar -type fpara -type dresolver isso, certo? Também esperará a resposta do slm com relação ao objetivo de-exec basename {}
user66001 2/13/13
@ user66001 sim, -type dencontrará diretórios. A basenameé uma idéia muito boa, ele irá imprimir apenas o nome e remover o caminho. Supondo que você queira apenas nomes, é isso que você deve fazer. Dê uma olhada man basenamee também man dirname.
terdon
Thanks terdon - Desculpe por não marcar o seu como a resposta. Senti que a versão atual do slm 's tem mais informações, para aqueles que precisam.
user66001
11
@ user66001 Em primeiro lugar, você está absolutamente certo, o slm's é realmente melhor. Segundo, você nunca deve se desculpar por não aceitar, pode haver apenas um e esse deve ser o que você considera melhor :).
terdon
6

A resposta aceita funciona corretamente, mas é um pouco ineficiente porque gera um novo basenameprocesso para cada subdiretório:

find /usr/share/festival/lib/voices -maxdepth 2 -mindepth 2 \
    -type d -exec basename {} \;

Quando possível, é preferível usar os recursos incorporados findpara evitar a despesa dos processos de desova. findpossui uma capacidade bastante extensa para modificar sua saída impressa usando a -printfação A -print ação padrão imprime o caminho inteiro, mas usando -printfuma string de formato é possível selecionar partes do caminho para impressão. Para extrair apenas a parte do nome do arquivo do caminho sem os diretórios principais (como basename faz), a cadeia de caracteres de formato é %f. Para colocar uma nova linha após cada nome de arquivo, inclua \no seguinte:

$ find /usr/share/festival/lib/voices -maxdepth 2 -mindepth 2 \
    -type d -printf '%f\n'
nitech_us_awb_arctic_hts
nitech_us_bdl_arctic_hts
nitech_us_slt_arctic_hts
nitech_us_jmk_arctic_hts
nitech_us_clb_arctic_hts
nitech_us_rms_arctic_hts
ked_diphone
kal_diphone
Michael Henry
fonte
+1 Obrigado pela sua resposta Michael. Também vejo a vantagem da maneira de fazer isso na sua resposta, mas, dado o trabalho posto na resposta da slm, estou pensando em mudar a resposta aceita. Se o @slm vê isso, e não tem problemas em escolher isso sobre o dele, voltarei aqui para alterar a resposta aceita.
user66001
11
A resposta do @ slm é bem explicada e cobre o padrão mais geral de uso findcom comandos externos arbitrários; é apenas menos eficiente para operações integradas find. Eu tinha pensado em adicionar um comentário à sua resposta, mas isso requer mais reputação do que eu. Não há necessidade de alterar sua resposta aceita, pois a resposta atualmente aceita está correta, bem explicada e utilizável como padrão para o caso mais geral; Eu só queria salientar que, para este caso específico, existe um método mais eficiente.
Michael Henry
0

TLDR; para aqueles que estão chegando aqui com base no título desta pergunta; para "Listar subdiretórios apenas com nível n profundo": use

find -maxdepth N

Onde Nestá qualquer número.

Gabriel Staples
fonte