List subdirectories only n level deep
I'm on Fedora, and these voicepacks are in a slightly different location:
$ 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
You can just modify this like so:
$ ls /usr/share/festival/voices/*/ -1 | grep -vE "/usr|^$"
Using find
Using ls
in this manor is typically frowned upon because the output of ls
is difficult to parse. Better to use the find
command, like so:
$ 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
Details of find & basename
This command works by producing a list of full paths to files that are exactly 2 levels deep with respect to this directory:
/usr/share/festival/lib/voices
This list looks like this:
$ 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
But we want the last part of these directories, the leaf node. So we can make use of basename
to parse it out:
$ basename /usr/share/festival/lib/voices/us/nitech_us_awb_arctic_hts
nitech_us_awb_arctic_hts
Putting it all together, we can make the find
command pass each 2 level deep directory to the basename
command. The notation basename {}
is what is doing these basename conversions. Find calls it via it's -exec
switch.
The easiest is
ls -d /usr/share/festival/voices/*/*
That is expanded by the shell into all sub directories of /usr/share/festival/voices/
and then to the contents of each of those sub directories.
If you only want to descend to a specific level as your title suggests, with some implementations of find
like GNU's and some BSD's:
find /usr/share/festival/voices/ -mindepth 2 -maxdepth 3 -type d
That will find all directories (-type d
) that are in a subdirectory of /usr/share/festival/voices/
because of mindepth 2
but are not deeper than 3 levels down (maxdepth 3
). From 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.
The accepted answer works correctly
but is somewhat inefficient because it spawns a new basename
process for
each subdirectory:
find /usr/share/festival/lib/voices -maxdepth 2 -mindepth 2 \
-type d -exec basename {} \;
When possible, it's preferable to use features built into find
to avoid the
expense of spawning processes. find
has a fairly extensive capability to
modify its printed output using the -printf
action. The default -print
action prints the entire path, but using -printf
and a format string it's
possible to select portions of the path for printing. To extract just the
filename portion of the path without the leading directories (as basename
does), the format string is %f
. To place a newline after each filename,
include \n
as follows:
$ 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