There are a lot of ways to read user input in Bash scripts. However, allowing a user to type input can pose some problems. The main issue is input validation, where you have to account for every possible input. An easy way around this is to create a multiple choice menu for your Bash scripts. Using this method allows you to define a simple, predetermined set of options the user can choose from. In this quick tip we will discuss using a select construct and case statements to create a simple multiple choice menu in Bash scripts.

First let's take a look at an example multiple choice menu script, then we will break it down and explain each section.

#!/bin/bash
PS3='Choose your favorite food: '
foods=("Pizza" "Pho" "Tacos" "Quit")
select fav in "${foods[@]}"; do
    case $fav in
        "Pizza")
            echo "Americans eat roughly 100 acres of $fav each day!"
	    # optionally call a function or run some code here
            ;;
        "Pho")
            echo "$fav is a Vietnamese soup that is commonly mispronounced like go, instead of duh."
	    # optionally call a function or run some code here
            ;;
        "Tacos")
            echo "According to NationalTacoDay.com, Americans are eating 4.5 billion $fav each year."
	    # optionally call a function or run some code here
	    break
            ;;
	"Quit")
	    echo "User requested exit"
	    exit
	    ;;
        *) echo "invalid option $REPLY";;
    esac
done

Here is an animation of the script in action. Pay careful attention to the choices and the output of each one. Take note of how after selecting some options it asks for another selection. But, when we choose Tacos, it exits back to the prompt.

Animation of Bash multiple choice menu in action

Multiple Choice Menu Script Explained

Now let's dissect each part of the script. This will help you understand how the multiple choice menu is created, and how you can edit it to fit your needs.

The first line is the shebang, it tells the process running the script which interpreter to use. In this case it is bash.

#!/bin/bash

Setting Up The Input Prompt

The next line sets the PS3 variable. This is the prompt used by the select statement when asking for a selection from our multiple choice menu.

PS3='Choose your favorite food: '

Creating Your List of Predetermined Options

This line creates an array of the options available for the user to select.

foods=("Pizza" "Pho" "Tacos" "Quit")

Creating The Menu With Select

Here we start the select construct which creates the menu. In this line we are telling select to create a menu from the foods array. We are also going to set the users selection in the $fav variable. The select construct has a similar syntax to a for loop, this is why you see the do statement at the end.

select fav in "${foods[@]}"; do

Create More Robust Options with a Case Statement

Case statements allow you to selectively execute a command that corresponds with the first matched pattern. For example, if we select Pizza from the multiple choice menu, it will execute the command-list that corresponds to that word. For more in-depth information read "Using case Statements in Bash Scripting".

In the case statement we have the opening stanza. This tells case to search for an options that matching the value of the $fav variable.

    case $fav in

Next we have a bunch of clauses outlining each option. It starts with the option (or options) to match, then the code that should be run if a match is found. All of our clauses end with ;; which is the termination string. These are explained in-depth in our case statement article. This clause says if the value of $fav matches Pizza run the echo command.

        "Pizza")
            echo "Americans eat roughly 100 acres of $fav each day!"
	    # optionally call a function or run some code here
            ;;

Before the animation above I asked that you pay attention to the choices being made, and the output of each change. I wanted to point out that every time a choice is selected, the code will run then drop you back into the select loop.

The Tacos clause shows you how to add the break command. This tells the script to break out of the loop. So if you select Pizza, it will run the echo command that corresponds with that selection, then ask for another select (because there is no break command). However, if you select Tacos, it will run the echo command that corresponds with the Tacos clause and then break the loop and continue with the script.

        "Tacos")
            echo "According to NationalTacoDay.com, Americans are eating 4.5 billion $fav each year."
	    # optionally call a function or run some code here
	    break
            ;;

Remember in the introduction we mentioned accounting for all possible inputs? Well this next line helps a little with that premise. This is a "catch all". It uses the asterisks wildcard to catch any input that doesn't match one of our predetermined choices. Our valid choices are 1 through 4, if the user inputs 8 (or anything other than 1-4), this line will match and tell them it is invalid input.

 *) echo "invalid option $REPLY";;

Here is an example of how the catch all works:

[savona@putor TMP]$ ./test-menu.sh 
1) Pizza
2) Pho
3) Tacos
4) Quit
Choose your favorite food: 8
invalid option 8
Choose your favorite food: 

Closing the Constructs and Loops

After all the match clauses, we end the case statement with esac which is case backwards. This is similar to an if statement that closes with fi.

    esac

The last line closes the select construct, that was opened with do.

done

Conclusion

In this Linux quick tip we showed you how to create a multiple choice menu in a Bash script. It uses several different looping and conditional constructs which can make it kind of intimidating at first. However, once you understand the logic, it is fairly simple.

If you have any questions or comments I would love to hear them in the comments below. We also have links to the relevant parts of the Bash manual below for continued reading.