Lista de subdirectorios solo n nivel profundo

58

Festival almacena los datos del paquete de voz en la siguiente estructura de directorios de ejemplo:

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

¿Cuál es la línea simple más simple (preferiblemente usando ls) para imprimir solo el <voicepack name>'s, en todos los <language>subdirectorios potencialmente numerosos ?

user66001
fuente

Respuestas:

80

Estoy en Fedora, y estos paquetes de voz están en una ubicación ligeramente 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

Simplemente puede modificar esto así:

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

Usando find

El uso lsen esta mansión generalmente está mal visto porque la salida de lses difícil de analizar. Es mejor usar el findcomando, así:

$ 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

Detalles de find & basename

Este comando funciona produciendo una lista de rutas completas a los archivos que tienen exactamente 2 niveles de profundidad con respecto a este directorio:

/usr/share/festival/lib/voices

Esta lista se ve así:

$ 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

Pero queremos la última parte de estos directorios, el nodo hoja. Entonces podemos usar basenamepara analizarlo:

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

Poniendo todo junto, podemos hacer que el findcomando pase cada directorio profundo de 2 niveles al basenamecomando. La notación basename {}es lo que está haciendo estas conversiones de nombre base. Encuentra llamadas a través de su -execinterruptor.

slm
fuente
jajaja, casi exactamente la misma respuesta, grandes mentes y todo eso :).
terdon
+1 - Para aquellos que se tropiezan al descubrir qué -exec basename {}hace, ¿podrían explicarlo aquí?
user66001
@ user66001: avíseme si eso lo explica lo suficiente.
slm
@ user66001 - puede aceptar una de las respuestas si resuelve su problema a su satisfacción 8-)
slm
1
El comando find es lo que necesito el 99% del tiempo. El límite tanto max como min fue clave: solo hice max. Ejemplo: find ~/ -maxdepth 1 -mindepth 1 -type d | xargs du -csh | sort -h Encuentra los directorios más grandes ordenados por tamaño
oligofren
23

Lo más fácil es

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

El shell lo expande a todos los subdirectorios /usr/share/festival/voices/y luego a los contenidos de cada uno de esos subdirectorios.

Si solo desea descender a un nivel específico como sugiere su título, con algunas implementaciones findcomo GNU y algunas BSD:

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

Eso encontrará todos los directorios ( -type d) que están en un subdirectorio /usr/share/festival/voices/debido a, mindepth 2pero no son más profundos que 3 niveles hacia abajo ( 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
fuente
Sí, es como mirarse en un espejo 8-)
slm
+1 Cómo ambos obtuvieron 2 votos es interesante. La votación cruzada explica 1 cada uno;) PD: quería nombres de directorio, así que solo cambiar -type fa -type ddebería resolver esto, ¿verdad? También esperará la respuesta de slm con respecto al propósito de-exec basename {}
user66001
@ user66001 sí, -type dencontrará directorios. El basenamees una muy buena idea, se imprimirá sólo el nombre y eliminar la ruta. Asumiendo que solo quieres nombres, eso es lo que debes hacer. Echa un vistazo man basenamey también man dirname.
terdon
Gracias terdon. Perdón por no marcar tu respuesta. Sentía que la versión actual del SLM 's tiene más información, para aquellos que lo necesitan.
user66001
1
@ user66001 en primer lugar, tienes toda la razón, slm es realmente mejor. Segundo, nunca debes disculparte por no aceptar, solo puede haber uno y ese debería ser el que consideres mejor :).
terdon
6

La respuesta aceptada funciona correctamente pero es algo ineficiente porque genera un nuevo basenameproceso para cada subdirectorio:

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

Cuando sea posible, es preferible usar funciones integradas findpara evitar el gasto de los procesos de generación. findtiene una capacidad bastante amplia para modificar su salida impresa usando la -printfacción. La -print acción predeterminada imprime la ruta completa, pero utilizando -printfy una cadena de formato es posible seleccionar partes de la ruta para imprimir. Para extraer solo la parte del nombre de archivo de la ruta sin los directorios iniciales (como lo basename hace), la cadena de formato es %f. Para colocar una nueva línea después de cada nombre de archivo, incluya \nlo siguiente:

$ 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
fuente
+1 Gracias por tu respuesta Michael. Puedo ver la ventaja de la forma de hacer esto en su respuesta, también, pero dado el trabajo puesto en la respuesta de slm, tengo dudas acerca de cambiar la respuesta aceptada. Si @slm ve esto y no tiene problemas para elegir esto sobre el suyo, volveré aquí para cambiar la respuesta aceptada.
user66001
1
La respuesta de @ slm está bien explicada y cubre el patrón más general de uso findcon comandos externos arbitrarios; es menos eficiente para las operaciones integradas find. Había considerado agregar un comentario a su respuesta, pero eso requiere más reputación que la que tengo. No es necesario cambiar su respuesta aceptada, ya que la respuesta aceptada actualmente es correcta, está bien explicada y puede utilizarse como un patrón para el caso más general; Solo quería señalar que para este caso específico hay un método más eficiente.
Michael Henry
0

TLDR; para aquellos que acaban de llegar basados ​​en el título de esta pregunta; para "Listar subdirectorios solo n nivel profundo": use

find -maxdepth N

¿Dónde Nestá cualquier número?

Gabriel Staples
fuente