1

I am working on a multi-level menu and I want to implement the ability for sub menus and sub-sub menus to return to the previous menu or go directly back to the main menu. Please see below...

[Menu]
1 - Option 1
2 - Option 2
3 - Quit (exit command)

Please choose option:

#Under Option 1
1 - Sub-Option 1
2 - Sub-Option 2
3 - back to main menu

#Under Sub-Option 1
1 - Sub-sub Option 1
2 - Sub-sub Option 2 
3 - back to Sub-option menu
4 - back to main menu 

My current code keeps each menu in a separate function. I included my Sub-Option menu below...

suite_menu() {
    show_suite_menu

    read -p "Pick Number> " choice

    case $choice in

        1)  echo "1 selected."
            fullsuite_menu ;;
        2)  echo "2 selected."
            fullsuite_menu ;;
        3)  echo "3 selected."
            fullsuite_menu ;;
        0)  break;;
        *) echo "Invalid choice, please try again";;
    esac
}

How could I code my sub-sub-option menu to have option 3 going to the menu above, and option 0 going to main menu? Thanks in advance <3

4
  • 4
    That feels more like a general imperative/procedural programming problem. And frankly, it might be something shell scripting is not optimally equipped to deal with, even with a relatively mighty shell like bash. Is there a specific reason you want to do this with bash, and not any programming language you're familiar with? Commented Mar 5 at 17:32
  • A solution would be thst every time you call a function to draw a new submenu, you invoke it with list of call backs to the previous menu functions that have already been invoked. But as I tred to craft a prototype of such a solution, i came to the same conclusion as another commentor has suggested: this is a pain to do in the shell. Commented Mar 5 at 19:05
  • I see, thank you for the responses. Might replace this with something else then, thanks again! Commented Mar 5 at 19:43
  • Are you just reimplementing Bash's select statement? This seems like reinventing a wheel. Commented Mar 10 at 13:55

2 Answers 2

1

Initial thoughts were towards maintaining some sort of call stack for the menu/functions, with return codes (or more likely variable settings) determining how the 'parent' menu/function is to function upon return from the child menu/function call. And I'd probably look at something along this lines if there's a need to maintain some sort of recursive data sets (eg, appending to data when going 'down', stripping off data when going 'up') but there's nothing in OP's question indicating this level of data manipulation so ...

A simplistic approach would be to have each menu choice/action designate the 'next' command (eg, break, <function_name>) to be called, with a simple while true loop at the top level executing the 'next' command.

Implementing OP's sample menus with some verbose bash code, with undefined actions/menus implemented as simple echo calls:

##########

menu_main() {

printf "\n#### current function: ${FUNCNAME}\n"

local menu choice

menu="
1 - Option 1
2 - Option 2
3 - Quit (exit command)
"

unset next

while true
do
    echo "${menu}"
    read -p "Pick Number> " choice

    case $choice in
        1) next=menu_1 ;;
        2) echo "choice #${choice}, do something" ;;
        3) next=break   ;;
        *) echo "Invalid choice, please try again";;
    esac

    [[ -n "${next}" ]] && break
done
}

##########

menu_1() {

printf "\n#### current function: ${FUNCNAME}\n"

local menu choice

menu="
1 - Sub-Option 1
2 - Sub-Option 2
3 - back to main menu
"

unset next

while true
do
    echo "${menu}"
    read -p "Pick Number> " choice

    case $choice in
        1) next=menu_1_1  ;;
        2) echo "choice #${choice}, do something" ;;
        3) next=menu_main ;;
        *) echo "Invalid choice, please try again";;
    esac

    [[ -n "${next}" ]] && break
done
}

##########

menu_1_1() {

printf "\n#### current function: ${FUNCNAME}\n"

local menu choice

menu="
1 - Sub-Sub-Option 1
2 - Sub-Sub-Option 2
3 - back Sub-option menu
4 - back to main menu
"

unset next

while true
do
    echo "${menu}"
    read -p "Pick Number> " choice

    case $choice in
        1) echo "choice #${choice}, do something" ;;
        2) echo "choice #${choice}, do something" ;;
        3) next=menu_1 ;;
        4) next=menu_main ;;
        *) echo "Invalid choice, please try again";;
    esac

    [[ -n "${next}" ]] && break
done
}

NOTES:

  • this code merely demonstrates the overall concept
  • several variations could be made including the use of select, the use of a single 'generic' menu function that uses input args to access a specific menu's items/actions (stored in arrays), etc
  • unlike OP's sample code that shows a nested function call, this approach makes NO nested function calls; OP is responsible for insuring each function, upon returning, instructs the parent while true loop as to the 'next' operation

Taking for a test drive:

next=menu_main

while true
do
    echo "#### next: ${next}"
    ${next}
done

The output from exercising all menu options:

#### next: menu_main

#### current function: menu_main

1 - Option 1
2 - Option 2
3 - Quit (exit command)

Pick Number> 1
#### next: menu_1

#### current function: menu_1

1 - Sub-Option 1
2 - Sub-Option 2
3 - back to main menu

Pick Number> 1
#### next: menu_1_1

#### current function: menu_1_1

1 - Sub-Sub-Option 1
2 - Sub-Sub-Option 2
3 - back Sub-option menu
4 - back to main menu

Pick Number> 1
choice #1, do something

1 - Sub-Sub-Option 1
2 - Sub-Sub-Option 2
3 - back Sub-option menu
4 - back to main menu

Pick Number> 2
choice #2, do something

1 - Sub-Sub-Option 1
2 - Sub-Sub-Option 2
3 - back Sub-option menu
4 - back to main menu

Pick Number> 3
#### next: menu_1

#### current function: menu_1

1 - Sub-Option 1
2 - Sub-Option 2
3 - back to main menu

Pick Number> 1
#### next: menu_1_1

#### current function: menu_1_1

1 - Sub-Sub-Option 1
2 - Sub-Sub-Option 2
3 - back Sub-option menu
4 - back to main menu

Pick Number> 4
#### next: menu_main

#### current function: menu_main

1 - Option 1
2 - Option 2
3 - Quit (exit command)

Pick Number> 1
#### next: menu_1

#### current function: menu_1

1 - Sub-Option 1
2 - Sub-Option 2
3 - back to main menu

Pick Number> 2
choice #2, do something

1 - Sub-Option 1
2 - Sub-Option 2
3 - back to main menu

Pick Number> 3
#### next: menu_main

#### current function: menu_main

1 - Option 1
2 - Option 2
3 - Quit (exit command)

Pick Number> 2
choice #2, do something

1 - Option 1
2 - Option 2
3 - Quit (exit command)

Pick Number> 3
#### next: break

$                      # return back to the console's command prompt
2
  • Wow thank you this was super helpful. It gave me the idea to call the previous menu's function and it ended up working. Thanks so much :) Commented Mar 5 at 20:31
  • 1
    Look into the select command Commented Mar 5 at 20:31
0

You picked an answer, but this is my solution.

#!/bin/bash

function fullsuite_menu() {
    echo "Doing full suite menu for $1."
    nextmenu=main
}

function main() {
    select main in "Option 1" "Option 2" "Quit (exit command)"; do
        case $main in
            "Option 1") echo "1 selected."
                        nextmenu=option_1
                        break;;
            "Option 2") echo "2 selected."
                        nextmenu=option_2
                        break;;
            "Quit (exit command)") echo "3 selected."
                                   nextmenu=exit
                                   break;;
            *) echo "Invalid choice, please try again";;
        esac
    done
}

function option() {
    echo "Under Option $1"
    select option in "Sub-Option 1" "Sub-Option 2" "back to main menu"; do
        case $option in
            "Sub-Option 1") echo "1 selected."
                            nextmenu=sub_option_$1_1
                            break;;
            "Sub-Option 2") echo "2 selected."
                            nextmenu=sub_option_$1_2
                            break;;
            "back to main menu") echo "3 selected."
                                 nextmenu=main
                                 break;;
            *) echo "Invalid choice, please try again";;
        esac
    done
}

function sub_option() {
    echo "Under Sub-Option $1 $2"
    select option in "Sub-sub Option 1" "Sub-sub Option 2" "back to Sub-option menu" "back to main menu"; do
        case $option in
            "Sub-sub Option 1") echo "1 selected."
                                fullsuite_menu $option_$1_$2
                                break;;
            "Sub-sub Option 2") echo "2 selected."
                                fullsuite_menu $option_$1_$2
                                break;;
            "back to Sub-option menu") echo "3 selected."
                                       nextmenu=option_$1
                                       break;;
            "back to main menu") echo "3 selected."
                                 nextmenu=main
                                 break;;
            *) echo "Invalid choice, please try again";;
        esac
    done
}

function option_1() {
    option 1
}

function option_2() {
    option 2
}

function sub_option_1_1() {
    sub_option 1 1
}

function sub_option_2_1() {
    sub_option 2 1
}

function sub_option_1_2() {
    sub_option 1 2
}

function sub_option_2_2() {
    sub_option 2 2
}

function suite_menu() {
    PS3="Pick Number> "
    nextmenu=main
    while true; do $nextmenu; done
}

suite_menu

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.