Listar subdiretórios somente n nível profundo

42

O Festival armazena dados de voicepack na seguinte estrutura de diretório de exemplo:

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

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

    
por user66001 02.10.2013 / 20:20

3 respostas

62

Eu estou no Fedora e essas voicepacks 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 da seguinte maneira:

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

Usando o find

O uso de ls nessa mansão geralmente é desaprovado porque a saída de ls é difícil de analisar. Melhor usar o comando find , da seguinte forma:

$ 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 de encontrar & basename

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

Esta 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ó folha. Portanto, podemos usar basename para analisá-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 comando find passe cada diretório deep de 2 níveis para o comando basename . A notação basename {} é o que está fazendo essas conversões de nome de base. Find chama por meio de sua -exec switch.

    
por 02.10.2013 / 20:37
21

O mais fácil é

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

Isso é expandido pelo shell em todos os subdiretórios de /usr/share/festival/voices/ e depois no conteúdo de cada um desses sub-diretórios.

Se você quiser apenas descer a um nível específico, como o seu título sugere, com algumas implementações de find como as do GNU e algumas do BSD:

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

Isso localizará todos os diretórios ( -type d ) que estão em um subdiretório de /usr/share/festival/voices/ devido a mindepth 2 , mas não estão abaixo de 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.
    
por 02.10.2013 / 20:37
4

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

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

Quando possível, é preferível usar recursos incorporados em find para evitar despesa de processos de desova. find tem uma capacidade bastante extensa de modifique sua saída impressa usando a ação -printf . O padrão -print action imprime todo o caminho, mas usando -printf e uma string de formato é possível selecionar partes do caminho para impressão. Para extrair apenas o parte do nome do arquivo do caminho sem os diretórios principais (como basename faz), a string de formato é %f . Para colocar uma nova linha após cada nome de arquivo, inclua \n da seguinte forma:

$ 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
    
por 08.01.2018 / 14:29

Tags