List subdirectories only n level deep

  • Festival stores voicepack data in the following example directory structure:

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

    What is the simplest one-liner (preferably using ls) to print out just the <voicepack name>'s, in all the potentially numerous <language> subdirectories?

  • slm

    slm Correct answer

    7 years ago

    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.

    lol, pretty much _exactly_ the same answer, great minds and all that :).

    +1 - For those who get tripped up when figuring out what `-exec basename {}` does, could you explain here?

    @user66001 - let me know if that explains it enough.

    @user66001 - you can accept one of the answers if it solves your problem to your satisfication 8-)

    The find command is what I need 99% of the time. Limit both max and min was key - I only did max. Example: `find ~/ -maxdepth 1 -mindepth 1 -type d | xargs du -csh | sort -h` Finds the biggest directories sorted on size

License under CC-BY-SA with attribution


Content dated before 6/26/2020 9:53 AM