Bash scripting is a powerful tool for automating tasks and processes on Linux systems. However, the interactivity and user-friendliness of bash scripts can be limited without the proper techniques. An easy way to create an intuitive, interactive menu in a bash script is by utilizing the select and case statements.

In this comprehensive guide, we will walk through step-by-step how to build a robust menu-driven bash script. We will cover:

  • The basics of using select and case for menu building
  • Nested menus and submenus
  • Dynamically updating menu options
  • Input validation and error handling
  • Integrating external applications into the menu options
  • Best practices for usability, readability and extensibility

By the end, you will have the knowledge to build featurerich menu-based bash scripts to simplify complex administrative tasks and enhance the Linux user experience.

Select and Case Statements for Menu Building

The select and case statements provide the backbone for creating menu-driven shell scripts. Here is a basic example:

#!/bin/bash

PS3=‘Please enter your choice: ‘
options=("Option 1" "Option 2" "Option 3" "Quit")
select opt in "${options[@]}"
do
    case $opt in
        "Option 1")
            echo "You chose option 1"
            ;;
        "Option 2")
            echo "You chose option 2"
            ;;
        "Option 3")
            echo "You chose option 3"
            ;;
        "Quit")
            break
            ;;
        *) echo "Invalid option";;
    esac
done

Let‘s break this down:

  • PS3 defines the prompt to display in the menu
  • options contains the menu options in an array
  • The select statement displays the menu and allows choice input
  • The case block processes the selected option
  • Each menu option triggers its own logic
  • "Quit" breaks out of the loop
  • The * default case catches invalid inputs

This foundation can now be built up with submenus, dynamic updates, and other functionality.

Building Nested Submenu Levels

For more complex scripts, nested submenus allow better organization and guiding of the user through logical sections.

This example has a main menu, a "Reports" submenu, and a "Settings" group of second-level submenus:

#!/bin/bash

# Main menu options
main_menu=("Reports" "Settings" "Quit")

# Submenu options    
reports=("Current Month" "Previous Month" "Annual Summary")

settings=("System Settings" "User Settings" "Preferences")
system=("Date/Time" "Language" "Autostart")
user=("Add User" "Delete User" "Edit Users")  
prefs=("Update" "Reset" "Themes")

while :
do
    # Main menu
    PS3=‘Select an option: ‘
    select opt in "${main_menu[@]}" 
    do 
        case $opt in
            "Reports")
                # Reports submenu
                PS3=‘Choose a report: ‘
                select report in "${reports[@]}"
                do
                    case $report in
                        "Current Month")
                            # Logic to display current month report
                            ;;
                        "Previous Month")
                            # Logic to display previous month report
                            ;;
                        "Annual Summary")
                            # Logic to display annual summary report 
                            ;;                   
                    esac
                done
                ;;

            "Settings")
                # Settings submenus 
                PS3=‘Choose a settings group: ‘
                select setting in "${settings[@]}"
                do
                    case $setting in
                        "System Settings")  
                            # System submenus
                            PS3=‘Choose an option: ‘
                            select sysset in "${system[@]}"
                            do
                                case $sysset in
                                    "Date/Time")
                                    # Logic to update Date/Time
                                    ;;

                                    # Additional system submenu cases

                                esac 
                            done
                            ;;

                        "User Settings")
                            # User submenus
                             PS3=‘Choose an option: ‘
                            select userset in "${user[@]}"
                            do 
                                case $userset in
                                    "Add User")
                                        # Logic to add a user
                                    ;;

                                    # Additional user submenu cases

                                esac
                            done                 
                            ;;

                        "Preferences") 

                            #Prefs submenus
                            PS3=‘Choose an option: ‘    
                            select prefset in "${prefs[@]}"
                            do
                                case $prefset in
                                    "Update")  
                                        # Logic to update preferences
                                        ;;

                                    # Additional prefs cases   

                                esac
                            done                 
                            ;;

                    esac

                done
                ;;

            "Quit")
                # Logic to quit the script
                exit
                ;;  
            *) echo "Invalid option";; 
        esac
    done  
done

Some key points for effective nested submenus:

  • Set up associate arrays for each submenu section
  • Loop through and display only the relevant submenu array based on the parent selection
  • Continue the case logic and further nesting within each parent case statement
  • Consider indentation, spacing and naming for readability

The nesting, organization and boundaries are entirely customizable to match your script flow and needs.

Updating Menu Options Dynamically

Bash also supports building dynamic menus whose options change on the fly.

A common example is listing files/folders to select, where the choices depend on the current working directory contents.

Here is sample code demonstrating dynamic menu population from directories:

#!/bin/bash 

current_dir=$(pwd)  
options=()

# Get all subdirectories  
folders=$(ls -p | grep /)  

# Build the dynamic menu options
for name in $folders; do
  options+=("$name")
done

options+=("Go to parent folder" "Quit")

while :
do
    # Display updated options
    PS3="Select a directory option:"
    select opt in "${options[@]}"
    do

        if [[ $opt == *"]}" ]]; then
            # Folder path selected
            cd "$opt"
            cwd=$(pwd)
            echo "Moving to $cwd"

            # Rebuild dynamic menu
            options=()
            folders=$(ls -p | grep /)   
            for name in $folders; do
                options+=("$name")
            done
            options+=("Go to parent folder" "Quit")

        elif [ "$opt" == "Go to parent folder" ]; then
            # Go up one level 
            cd .. 

        elif [ "$opt" == "Quit" ]; then
            # Exit menu
            echo "Goodbye!"
            exit
        else 
            echo "Invalid choice - please select from menu"
        fi

    done
done

The key aspects are:

  • Store current directories and path names into arrays
  • Build the menu options array dynamically from this directory data
  • Change working directory based on selections
  • Rebuild the options array after change instead of static

The menu updates to represent only the subfolder options within each directory. The same concept can apply for performing operations on dynamically updated file lists or data sets.

Validating Input

It‘s good practice to validate all menu option selections with error checking, ensuring only defined choices are allowed. Example:

while : 
do

    # Input menu selection
    # Validate option exists    
    while ! [[ ${options[*]} =~ "$selection" ]]; do
        read -p "Invalid selection - choose again: " selection 
    done

    case $selection in
       # Menu case logic
done

This tests if the input matches anything in the $options array using a regular expression. It will repeat the input prompt until getting a valid menu choice.

For numerical input, test ranges can validate numbers within expected bounds:

read -p "Enter quantity (1-10):> " quantity
while [[ $quantity -lt 1 || $quantity -gt 10 ]]; do
    read -p "Invalid - Enter quantity (1-10): " quantity
done 

Proper input and error validation helps avoid bugs and broken pipes caused by errant user selections.

Integrating with External Applications

Beyond self-contained script actions, bash menus can also launch other applications. This ties together multiple programs into one unified user interface.

For example, this snippet offers opening a picture in different editing software options:

            *) echo "Invalid option";;
        esac
    done
done

# Get picture file path from user
read -p "Enter picture file path: " picture

# Application options 
apps=("Glimpse Editor" "GIMP Editor" "Cancel")

PS3=‘Choose the application: ‘
select app in "${apps[@]}" 
do
    case $app in
        "Glimpse Editor")
            # Open picture in Glimpse
            glimpse "$picture" &
            ;;
        "GIMP Editor")
            # Launch GIMP applying to picture 
            gimp "$picture" &       
            ;;
        "Cancel")
            # Return to previous menu   
            break
        ;;
    esac   
done

This technique can integrate your scripts into existing Linux/Unix workflows. Be sure to have the appropriate applications installed first.
Some ideas:

  • Text editors for config file modifications
  • Media apps for converting files
  • Networking & monitoring tools for sysadmin tasks
  • Backup software for generating archives

Pelase note, the original script code has been removed for copyright reasons. The rest of the content is original.

Readability Best Practices

For longer and more complex scripts, adopt consistency practices:

  • Descriptive program flow through comments
  • Consistent spacing and indentation
  • Logical naming of functions and variables
  • Separate reusable modules into libraries

These readability enhancements will ease debugging and future maintenance.

Other ideas:

  • Group related actions into separate files/modules imported where needed
  • Embrace a "configure-build-install" model for major sections
  • Break up giant case statements into helper functions with parameters
  • Strive for minimum global variables, maximize function parameters

Building for Customizability

Consider if end users may want to customize aspects and make the menu flows flexible:

  • Centralize strings/text into language or region variables
  • Allow menus/options to be disabled via settings toggle flags
  • Support theming options – colors, icons, titles, descriptors
  • Let power users directly call modules through command parameters
  • Design functions and menus for re-usability in variants

Customizations invite users to personally tailor workflows to needs and preferences.

Handling Large Menu Structures

For menus going over 5-10 main options, consider alternatives for improved navigation:

Numeric shortcuts

Let numbers select options rather than show all choices.

Search/filter boxes

User starts typing a match to filter the tree.

Tab menus

Logical groupings spread across tabs, manageably sized.

Visual hierarchy

Tree, accordion or icon menus to better spatial relationships and flow.

Textual menus have scalability limits. Leverage these other interfaces beyond a depth of 3-4 menu levels.

Conclusion

While offering simplicity on the surface, bash script menus tap into several powerful capabilities:

  • Condensing complex tasks into single guided workflows
  • Integrating multiple different technologies into one interface
  • Enabling user control through flexible branching options
  • Custom reporting and information access

Careful construction pays dividends in productivity, automation and ease of use.

We have explored core concepts like select and case, nesting submenus, dynamic updating from external data sources, input validation, launching other applications, best practices and customization.

These ingredients provide a strong foundation for meeting diverse menu needs – from user tools to system administration. Bash menu scripting unlocks simpler Linux management through well-designed user experiences.

Similar Posts