Shell Script Loop: Bash Select Menus

Shell Script Loop: Bash Select Menus

A shell script can offer actions as numbered choices. Bash select displays those choices, reads a selection, and repeats the menu body until break ends the loop.

This piece explains how Bash select works and shows how to build a menu and check its response.

TL;DR: Build a menu with Bash select

Bash select prints numbered choices, reads a selection, and repeats its body until you break out of the loop.

  • Set PS3 to label the prompt.
  • Use case to handle each choice and a default branch.
  • Use break for an exit choice.

What Is the select Loop in Bash?

The select compound command builds a numbered menu from words and runs a command list for each line of input. It belongs to Bash, so scripts that use it should name Bash in their shebang rather than assume every POSIX shell provides the construct.

Bash expands the words after in, displays each with a number, then reads a line using PS3 as the prompt. A matching number assigns the corresponding word to the menu variable, while REPLY keeps the input text. The body runs again after each choice unless a command such as break ends the loop.

The menu offers readable labels, and case maps them to actions.

How to Build a select Menu Step by Step

A small action menu needs a prompt, a list of labels, and a handler for every selection. Put the construct in a Bash script so its loop and exit behavior remain visible.

Step 1: Define the prompt and choices

Set PS3 before select and list the words users can choose. The example uses status, restart, and quit labels.

#!/usr/bin/env bash
PS3="Choose an action: "
select action in status restart quit; do
  case $action in
    status) printf 'Selected %s (option %s)\n' "$action" "$REPLY" ;;
    restart) printf 'Selected %s (option %s)\n' "$action" "$REPLY" ;;
    quit) printf 'Leaving menu.\n'; break ;;
    *) printf 'Invalid option: %s\n' "$REPLY" ;;
  esac
done

The menu variable contains the matched word, while REPLY retains the entered selection number. In a production script, replace the printf lines with the intended action and keep the default branch so unexpected input receives a response.

Step 2: Run the menu and inspect its response

Run the saved file with Bash and provide choices in order. The first input chooses restart, the second tests the default branch, and the third selects quit.

printf '2\n9\n3\n' | bash select_menu.sh
1) status
2) restart
3) quit
Choose an action: Selected restart (option 2)
Choose an action: Invalid option: 9
Choose an action: Leaving menu.

Bash select menu showing a valid choice, invalid input, and exit

I ran this exact input stream and the menu handled all three inputs before ending with exit status 0. The valid choice did not end the loop by itself, so the quit branch needs break.

Common Problems with a Bash select Loop

The menu needs deliberate handling for input that does not match a numbered choice. In my run, entering 9 printed “Invalid option: 9,” left the menu variable unmatched, and returned to the prompt.

Handle invalid choices, blank input, and EOF

Use the case statement’s default branch for text or numbers outside the menu. Bash leaves the selected word unmatched and keeps the entered line in REPLY, so the script can report the input without treating it as a command.

A blank line displays the menu again without running the body. End-of-file behaves differently: select terminates with a nonzero status when its input closes. Check that status when a script receives redirected or piped input.

CaseSymptom or behaviorFix
Valid menu numberThe menu variable receives its labelDispatch the recognized label in a case branch
Unlisted text or numberNo label matches. The input remains in REPLYUse the default branch to reject or explain the input
Blank lineThe menu and prompt appear againOffer a separate numbered exit choice
End-of-fileselect ends with nonzero statusCheck status when input is redirected or piped
Using a menu label as a commandA label such as status does not run a command by itselfMap each known label to an explicit case action, and never evaluate arbitrary REPLY text
Automating file or stream inputselect expects a menu number, not a record to processUse a read loop for records or streamed lines
Nested loopsbreak exits only the innermost loop by defaultUse a numbered break only when exiting multiple levels is intended

Conclusion

Bash select supplies the numbered menu and repeated prompt, while your case branches define what each choice does. Keep REPLY available for validation and break for a deliberate exit.

For the authoritative behavior, see the Bash Reference Manual on conditional constructs. To compare this menu with iteration over a known list, see Bash for loops in shell scripts and the site’s shell scripting and cron guide.

FAQ

These answers cover the prompt, input variable, and menu exit behavior.

How do I change the Bash select prompt?

Set PS3 before the select command. Bash displays that prompt each time it asks for a choice.

What does REPLY contain in a select loop?

REPLY contains the line entered at the prompt. The menu variable contains the matching choice label, or an empty value for an invalid selection.

How do I exit a select loop?

Run break in the branch that should leave the menu. Without break, select prompts again after the branch finishes.