#!/bin/bash
# Initialize an array to store selected options
declare -a selected_options
menu_items=("Option 1" "Option 2" "Option 3" "Option 4" "Done")
while true; do
clear
echo "Please select options (multiple selections allowed):"
# Display menu with selection indicators
for i in "${!menu_items[@]}"; do
# Check if current option is selected
if [[ " ${selected_options[*]} " == *" ${menu_items[$i]} "* ]]; then
printf "%d) %s +\n" $((i+1)) "${menu_items[$i]}"
else
printf "%d) %s\n" $((i+1)) "${menu_items[$i]}"
fi
done
read -p "Your choice: " choice
# Validate input
if ! [[ "$choice" =~ ^[0-9]+$ ]] || [ "$choice" -lt 1 ] || [ "$choice" -gt "${#menu_items[@]}" ]; then
echo "Invalid option. Please try again."
sleep 1
continue
fi
selected_index=$((choice-1))
selected_item="${menu_items[$selected_index]}"
# Handle Done selection
if [ "$selected_item" == "Done" ]; then
clear
if [ ${#selected_options[@]} -gt 0 ]; then
echo "You selected:"
printf "%s\n" "${selected_options[@]}" | sed ':a;N;$!ba;s/\n/, /g'
else
echo "No options were selected."
fi
exit 0
fi
# Toggle selection
if [[ " ${selected_options[*]} " == *" $selected_item "* ]]; then
# Remove from selected options
selected_options=("${selected_options[@]/$selected_item}")
# The above leaves empty elements, so we need to compact the array
temp=()
for item in "${selected_options[@]}"; do
[[ -n $item ]] && temp+=("$item")
done
selected_options=("${temp[@]}")
else
# Add to selected options
selected_options+=("$selected_item")
fi
done
This script demonstrates several important bash concepts:
- Array manipulation for tracking selected items
- Input validation to prevent errors
- Interactive menu refreshing using clear
- String manipulation for toggling selections
For more complex implementations, you might want to add:
# Color highlighting for selected items
printf "\033[32m%d) %s +\033[0m\n" $((i+1)) "${menu_items[$i]}"
# Multi-number input support (e.g., "1 3 4")
read -p "Your choices (space separated): " -a choices
for choice in "${choices[@]}"; do
# Process each choice
done
# Persistent selection display during input
read -p $'\033[7m'"Your choice (current: ${#selected_options[@]} selected)"$'\033[0m: ' choice
When working with bash menus:
- Always validate input to prevent script errors
- Use arrays carefully - bash array handling can be tricky
- Consider terminal compatibility issues with clear/formatting
- For production use, add timeout/quit options
For more sophisticated interfaces, consider:
# Using dialog or whiptail for native terminal dialogs
selected=$(dialog --checklist "Choose options:" 15 40 4 \
1 "Option 1" off \
2 "Option 2" off \
3 "Option 3" off \
4 "Option 4" off 2>&1 >/dev/tty)
Or external tools like fzf
for fuzzy-selection interfaces.
While bash's built-in select
statement provides basic menu functionality, it lacks native support for multiple selections. Here's a robust implementation that overcomes this limitation while maintaining clean script execution.
This solution uses associative arrays (requires bash 4.0+) to track selections and dynamically updates the menu display:
#!/bin/bash
# Options configuration
options=("Option 1" "Option 2" "Option 3" "Option 4" "Done")
declare -A selected
# Display function with toggle indicators
display_menu() {
clear
echo "Select options (multiple allowed):"
for i in "${!options[@]}"; do
marker=$([[ -n "${selected[$i]}" ]] && echo " +" || echo "")
printf "%d) %s%s\n" $((i+1)) "${options[$i]}" "$marker"
done
}
# Main selection loop
while true; do
display_menu
read -p "Your choice: " choice
# Validate input
if ! [[ "$choice" =~ ^[0-9]+$ ]] || (( choice < 1 || choice > ${#options[@]} )); then
echo "Invalid option"
sleep 1
continue
fi
# Handle Done selection
if (( choice == ${#options[@]} )); then
break
fi
# Toggle selection state
idx=$((choice-1))
if [[ -n "${selected[$idx]}" ]]; then
unset "selected[$idx]"
else
selected[$idx]=1
fi
done
# Output selected items
selected_options=()
for i in "${!selected[@]}"; do
selected_options+=("${options[$i]}")
done
echo "Selected options: $(IFS=,; echo "${selected_options[*]}")"
For a more polished implementation, consider adding:
- Color highlighting for selected items
- Input validation for non-numeric entries
- Horizontal display for space efficiency
- Support for checkbox-style toggling (using ANSI characters)
If your system has dialog
installed, you can leverage its built-in checklist widget:
#!/bin/bash
dialog --clear --checklist "Select options:" 15 40 10 \
1 "Option 1" off \
2 "Option 2" off \
3 "Option 3" off \
4 "Option 4" off \
2> selections.txt
selected=$(cat selections.txt)
echo "You selected: $selected"
rm -f selections.txt
For scripts with dozens of options, consider:
- Implementing pagination
- Adding search/filter capability
- Optimizing screen redraws
Always include proper error handling:
if (( ${#selected_options[@]} == 0 )); then
echo "No options selected" >&2
exit 1
fi