killstreet
killstreet

Reputation: 1332

Create select menu in bash

So I am trying to make a little custom management script for my server, but I am running into a problem for creating my menu script with the right results.

I got the following script

function config
{

list=''

declare -a programs=("docker" "IdontExist" "pushover" "IdontexistEither")

for program in "${programs[@]}"
do
    #Check if command exists on the server
    if hash $program 2>/dev/null; then
        list="${list} \"${program}\""
    fi
done



title="Config manager"
prompt="Pick an option:"
options=(${list})

echo "$title"
PS3="$prompt "

select opt in "${options[@]}" "Quit"; do
    case "$REPLY" in

    #Dont know how to create this section in a script
    1 ) echo "Update Docker"; break;;
    3 ) echo "Update IdontExist"; break;;
    2 ) echo "Update mytest"; break;;
    4 ) echo "Update IdontExistEither"; break;;

    $(( ${#options[@]}+1 )) ) echo "Goodbye!"; break;;
    *) echo "Invalid option. Try another one.";continue;;
    esac
done

}

My list variable will look as follow

list: "docker" "pushover"

So my options in the script above will not work accordingly. How can I create the options, depending on the list variable?

And I also want to call in certain functions depending on the selected option, for example if somebody choose "docker" I want to call a function named: _docker_config and when its "pushover" I will call a function named _pushover_config How would I also achieve that in the script?

Upvotes: 0

Views: 1684

Answers (1)

cdarke
cdarke

Reputation: 44444

The problem is that you are testing $REPLY instead of $opt:

select opt in "${options[@]}" "Quit"
do

    if [[ -z $opt ]]; then
        echo "Didn't understand \"$REPLY\" "
        REPLY=
    else
        case "$opt" in

        docker)
            echo "Update Docker"
            break;;
        IdontExist)
            echo "Update IdontExist"
            break;;
        pushover)
            echo "Update mytest"
            break;;
        IdontexistEither)
            echo "Update IdontExistEither"
            break;;
        Quit)
            echo "Goodbye!"
            break;;
        *)
           echo "Invalid option <$opt>. Try another one."
           ;;
        esac
    fi
done

If you use $opt then you don't need to match the menu numbers (which in your case are variable) with the actual entries (program names) - select does that for you. The only time you need $REPLY is when it is invalid (setting REPLY to an empty string redisplays the menu on the next iteration).

Not all of the choices in the case statement will be valid for every run, but that's OK because $opt will only get populated by the valid ones, and the others will not be visible to the user.

You don't need the continue, it does that for you by being in a loop.

Edit:

Here is an alternative using an Associative Array instead of a case statement. It enables the menu and the tests to be dynamic. In the case shown only two of the options have function, that does not have to be the case, it can be any number (with reason).

_docker_config()
{   
    echo "Welcome to docker"
}

_pushover_config()
{   
    echo "Welcome to pushover"
}

# This declares and associative array with the keys being the menu items
# and the values being the functions to be executed.  
# This will have to include all possible programs
declare -A functab=(["Docker"]=_docker_config 
                    ["Pushover"]=_pushover_config)

title="Config manager"
prompt="Pick an option:"

# The options are hard-coded here, of course you will dynamically generate it
# I omitted that for testing purposes
declare -a options=("Docker" "IdontExist" "Pushover" "IdontexistEither")

echo "$title"
PS3="$prompt "

select opt in "${options[@]}" "Quit"
do

    if [[ $opt == "Quit" ]]; then
        echo "Goodbye!"
        break
    fi

    if [[ -z $opt ]]; then
        echo "Didn't understand \"$REPLY\" " >&2
        REPLY=
    else
        # Here we check that the option is in the associative array
        if [[ -z "${functab[$opt]}" ]]
        then
            echo "Invalid option. Try another one." >&2
        else
            # Here we execute the function
            eval ${functab[$opt]}   # See note below
        fi
    fi
done

The eval is used with caution. Generally this is a command to avoid because it can be a security issue, however we are checking for valid entries so in this case it is justified. Just don't over-use it.

Upvotes: 2

Related Questions