mike atkinson
mike atkinson

Reputation: 47

bash list all subdirectories in a folder, write them to array to use in a menu

I am in the middle of writing a bash script, I have multiple subfolders in a certain directory, I want to list the names of the subfolders and read the results into an array omitting a certain single folder called 'cmmdm' from the results. Once I have read the names into the array I want to generate a menu with each submenu name as a choice which I will then perform a function on the given subfolder depending on which choice the user makes.

EDIT: sorry should have added my initial code:

#!/bin/bash
# - create array
declare -a CDARRAY

# - set 0 to exit in prep for menu
CDARRAY[0]=exit

# - create a counter to use in while loop
count=1

# - while loop to itterate through folder and add each folder except cmmdm into array
ls -d /home/nginx/domains/* | {
    while read CMMDOMAIN ; do

            if [ $CMMDOMAIN != "/home/nginx/domains/cmmdm" ]
            then
            $CDARRAY[$count]=$CMMDOMAIN
            echo $CDARRAY[$count]
            count=$[count + 1]
            fi

    done
}

This does go through the folders and does ignore 'cmmdm' however my code to add the variable CMMDOMAIN to the array is wrong. I have never written a script in bash before so I think probably I've gotten the syntax wrong or missing some braces or something

Upvotes: 4

Views: 8994

Answers (2)

Sigve Karolius
Sigve Karolius

Reputation: 1456

Could find be something for you?

DIRS=$(find ${PWD} -maxdepth 1 -type d)
echo ${DIRS}

Or maybe a for-loop (this will require you to make the array yourself)?

for DIR in $(find ${PWD} -maxdepth 1); do if $(test -d ${DIR}); then echo $(basename ${DIR}); fi; done

I have been told that using "Internal Field Separator" (IFS) as shown below is an even more safe solution (protecting against weirdness, e.g. white characters, in fild/directory names)

while IFS= read -r DIR; do echo "${DIR}"; done < <(find . -maxdepth 1 -type d -printf "%P\n")

Upvotes: 4

gniourf_gniourf
gniourf_gniourf

Reputation: 46843

Your code has a lot of issues, too many to be discussed here (no offense).

Here's a full example that will show a menu as you want, and does some common checking:

#!/bin/bash

shopt -s extglob nullglob

basedir=/home/nginx/domains

# You may omit the following subdirectories
# the syntax is that of extended globs, e.g.,
# omitdir="cmmdm|not_this_+([[:digit:]])|keep_away*"
# If you don't want to omit any subdirectories, leave empty: omitdir=
omitdir=cmmdm

# Create array
if [[ -z $omitdir ]]; then
   cdarray=( "$basedir"/*/ )
else
   cdarray=( "$basedir"/!($omitdir)/ )
fi
# remove leading basedir:
cdarray=( "${cdarray[@]#"$basedir/"}" )
# remove trailing backslash and insert Exit choice
cdarray=( Exit "${cdarray[@]%/}" )

# At this point you have a nice array cdarray, indexed from 0 (for Exit)
# that contains Exit and all the subdirectories of $basedir
# (except the omitted ones)
# You should check that you have at least one directory in there:
if ((${#cdarray[@]}<=1)); then
    printf 'No subdirectories found. Exiting.\n'
    exit 0
fi

# Display the menu:
printf 'Please choose from the following. Enter 0 to exit.\n'
for i in "${!cdarray[@]}"; do
    printf '   %d %s\n' "$i" "${cdarray[i]}"
done
printf '\n'

# Now wait for user input
while true; do
    read -e -r -p 'Your choice: ' choice
    # Check that user's choice is a valid number
    if [[ $choice = +([[:digit:]]) ]]; then
        # Force the number to be interpreted in radix 10
        ((choice=10#$choice))
        # Check that choice is a valid choice
        ((choice<${#cdarray[@]})) && break
    fi
    printf 'Invalid choice, please start again.\n'
done

# At this point, you're sure the variable choice contains
# a valid choice.
if ((choice==0)); then
    printf 'Good bye.\n'
    exit 0
fi

# Now you can work with subdirectory:
printf "You chose subdirectory \`%s'. It's a good choice.\n" "${cdarray[choice]}"

The comments should explain pretty clearly what's going on. The technique used to build the array, and that was the purpose of your question, is extended globs. For example:

shopt -s extglob nullglob
cdarray=( /home/nginx/domains/!(cmmdm)/ )

will populate cdarray with all subdirectories of /home/nginx/domains/ that don't match cmmdm (exact, full match). To have all subdirectories that don't end with a or b:

shopt -s extglob nullglob
cdarray=( /home/nginx/domains/!(*[ab])/ )

Upvotes: 7

Related Questions