Take the 2-minute tour ×
Server Fault is a question and answer site for professional system and network administrators. It's 100% free, no registration required.

I'm a bash newbie but I would like to create a script in which I'd like to allow the user to select multiple options from a list of options.

Essentially what I would like is something similar to the example below:

       #!/bin/bash
       OPTIONS="Hello Quit"
       select opt in $OPTIONS; do
           if [ "$opt" = "Quit" ]; then
            echo done
            exit
           elif [ "$opt" = "Hello" ]; then
            echo Hello World
           else
            clear
            echo bad option
           fi
       done

(sourced from http://www.faqs.org/docs/Linux-HOWTO/Bash-Prog-Intro-HOWTO.html#ss9.1)

However my script would have more options, and I'd like to allow multiples to be selected. So somethig like this:

1) Option 1 2) Option 2 3) Option 3 4) Option 4 5) Done

Having feedback on the ones they have selected would also be great, eg plus signs next to ones they ahve already selected. Eg if you select "1" I'd like to page to clear and reprint:

1) Option 1 +
2) Option 2
3) Option 3
4) Option 4
5) Done

Then if you select "3":

1) Option 1 +
2) Option 2
3) Option 3 +
4) Option 4
5) Done

Also, if they again selected (1) I'd like it to "deselect" the option:

1) Option 1
2) Option 2
3) Option 3 +
4) Option 4
5) Done

And finally when Done is pressed I'd like a list of the ones that were selected to be displayed before the program exits, eg if the current state is:

1) Option 1
2) Option 2 +
3) Option 3 + 
4) Option 4 +
5) Done

Pressing 5 should print:

Option 2, Option 3, Option 4

and the script terminate.

So my question - is this possible in bash, and if so is anyone able to provide a code sample?

Any advice would be much appreciated.

share|improve this question

4 Answers 4

up vote 18 down vote accepted

I think you should take a look at dialog or whiptail.

dialog box

Edit:

Here's an example script using the options from your question:

#!/bin/bash
cmd=(dialog --separate-output --checklist "Select options:" 22 76 16)
options=(1 "Option 1" off    # any option can be set to default to "on"
         2 "Option 2" off
         3 "Option 3" off
         4 "Option 4" off)
choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty)
clear
for choice in $choices
do
    case $choice in
        1)
            echo "First Option"
            ;;
        2)
            echo "Second Option"
            ;;
        3)
            echo "Third Option"
            ;;
        4)
            echo "Fourth Option"
            ;;
    esac
done
share|improve this answer
    
Thanks for that. Looks more complex than I had hoped, but I will check it out :-) –  user38939 May 28 '10 at 11:45
    
@am2605: See my edit. I added an example script. –  Dennis Williamson May 28 '10 at 15:14
2  
It only looks complex until you've used it once or twice, then you'll never use anything else... –  Chris S May 28 '10 at 15:19

If you think whiptail is complex, here it goes a bash-only code that does exactly what you want. It's short (~20 lines), but a bit cryptic for a begginner. Besides showing "+" for checked options, it also provides feedback for each user action ("invalid option", "option X was checked"/unchecked, etc).

That said, there you go!

Hope you enjoy... its was quite a fun challenge to make it :)

#!/bin/bash

# customize with your own.
options=("AAA" "BBB" "CCC" "DDD")

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
echo "$msg"
share|improve this answer
    
Good job! Good job! –  Daniel Jun 3 at 21:03
export supermode=none

source easybashgui

list "Option 1" "Option 2" "Option 3" "Option 4"
share|improve this answer
2  
Maybe you could add a little description of what this is doing? For future visitors, not so much for the OP. –  slm May 10 '13 at 8:16
    
Also, a link to the origin of easybashgui. –  Dennis Williamson May 10 '13 at 13:18

Here's a way to do exactly what you want using only Bash features with no external dependencies. It marks the current selections and allows you to toggle them.

#!/bin/bash
# Purpose: Demonstrate usage of select and case with toggleable flags to indicate choices
# 2013-05-10 - Dennis Williamson

choice () {
    local choice=$1
    if [[ ${opts[choice]} ]] # toggle
    then
        opts[choice]=
    else
        opts[choice]=+
    fi
}

PS3='Please enter your choice: '
while :
do
    clear
    options=("Option 1 ${opts[1]}" "Option 2 ${opts[2]}" "Option 3 ${opts[3]}" "Done")
    select opt in "${options[@]}"
    do
        case $opt in
            "Option 1 ${opts[1]}")
                choice 1
                break
                ;;
            "Option 2 ${opts[2]}")
                choice 2
                break
                ;;
            "Option 3 ${opts[3]}")
                choice 3
                break
                ;;
            "Option 4 ${opts[4]}")
                choice 4
                break
                ;;
            "Done")
                break 2
                ;;
            *) printf '%s\n' 'invalid option';;
        esac
    done
done

printf '%s\n' 'Options chosen:'
for opt in "${!opts[@]}"
do
    if [[ ${opts[opt]} ]]
    then
        printf '%s\n' "Option $opt"
    fi
done
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.