1

Before you tell me "there are tons of guides on this already" know that I actually have a select menu made and it is working.

However there is one thing that is driving me nuts. The way I have this menu coded currently this is what occurs:

1) User picks an option 2) Menu reloads 3) Option has an asterick next to it to show it is selected.

Problem:

The user has to select each option one at a time after the menu reloads after each selection. As you can imagine this is slow and make the terminal window messy.

What I want:

The user should be able to type 1-4 or 1,4,7 to select multiple options.

What I don't want:

A Whiptail or Dialog. I actually did make one as well and it is flawless. However I would prefer not to use it if I can help it or use it as a fallback. I think it would be more user friendly (not to mention easier on the eyes) to not use one.

Code:

#                           #
### CSGO Plugin selection ###
#                           #

options=(
         "SurfTimer - 2.02 - Core of this server."
         "AutoFileLoader - Caches all material, model, and sound files for players to download."
         "Chat-Procesor - Chat Processing Plugin"
         "Dynamic - PreReq for many plugins to work properly."
         "FixAngles - Fixes 'wrong angle on material' error that gets spammed in console when using store items"
         "Mapchooser_Extended - Map Vote System. See maplist.cfg/mapcycle.cfg.")
....


menu() {
    echo "Avaliable options:"
    for i in ${!options[@]}; do
        printf "%3d%s) %s\n" $((i+1)) "${choices[i]:- }" "${options[i]}"
    done
    [[ "$msg" ]] && echo "$msg"; :
}

prompt="Check an option (again to uncheck, ENTER when done): "
while menu && read -rp "$prompt" num && [[ "$num" ]]; do
    [[ "$num" != *[![:digit:]]* ]] &&
    (( num > 0 && num <= ${#options[@]} )) ||
    { msg="Invalid option: $num"; continue; }
    ((num--)); msg="${options[num]} was ${choices[num]:+un}checked"
    [[ "${choices[num]}" ]] && choices[num]="" || choices[num]="+"
done

printf "You selected"; msg=" nothing"
for i in ${!options[@]}; do
    [[ "${choices[i]}" ]] && { printf " %s" "${options[i]}"; msg=""; }
done
3
  • I wrote a little limerick, to show you something printed thick, there is a char, looks like a star, the word you want is asterisk.
    – dessert
    Mar 11, 2018 at 21:31
  • @dessert great. Are you gonna add anything meaningful to the discussion or are you just gonna correct people's grammar all day.
    – Sandman007
    Mar 11, 2018 at 21:54
  • I can do both. proud
    – dessert
    Mar 11, 2018 at 23:56

1 Answer 1

2

I'd do it as follows:

menu() {
    clear
    echo "Available options:"
    for i in ${!options[@]}; do
        printf "%3d%s) %s\n" $((i+1)) "${choices[i]:- }" "${options[i]}"
    done
}

prompt="Check an option (again to uncheck, ENTER when done): "
while menu && read -rp "$prompt" num && [[ "$num" ]]; do
    [[ "$num" =~ "-" ]] && num=$(seq $(sed -E 's/(\d*)-(\d*)/\1 \2/' <<<"$num"))
    for i in $num; do
      ((i--))
      [[ "${choices[i]}" ]] && choices[i]="" || choices[i]="*"
    done
done

This tests if $num contains a hyphen and builds the range if necessary, then simply loops over the content of $num so that users can give multiple options at once with e.g. 1 2 4 or 1-4 (but not a combination of those!). It also clears the terminal every time before the menu is printed.

3
  • Flawless! Thank You! Although I am having trouble with the range. I am pretty new to bash so I could just be putting it in the wrong spot. I assume it should go right after the for loop but inside the while loop?
    – Sandman007
    Mar 11, 2018 at 23:20
  • Hmm still not working. I did put it right before the for loop. But if you can't figure it out don't worry about it. You've already been a great help!
    – Sandman007
    Mar 11, 2018 at 23:41
  • @Sandman007 Really? I edited it in, it works perfectly for me with GNU bash 4.3.48(1)
    – dessert
    Mar 11, 2018 at 23:55

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .