I have tried searching the forum but i haven't found a solution for this.
I have a shell script that presents the users with menus. The menus branch out to sub menus. It is all hunky dory as long as i traverse forward. But if i am in a sub menu and return to the previous menu and choose any option, i am taken back to the sub menu. I can see where i am going wrong in my code as you can see highlighted below. But i cannot get around it unless i return to the main menu which has become quite cumbersome. Is there any way i can get around this by using goto labels? Any other modification of my code to get around this issue will be appreciated as well. Thanks in advance
function mainmenu {
echo "--------------- MAIN MENU -------------------"
echo " 1. CH1 "
echo " 2. CH2 "
echo " 3. CH3 "
echo "Choose 1-3"
read choice
case $choice in
1)
while :
do
submenu # calling another menu here
case $subchoice in
1)
do something
;;
2)
do something
;;
3)
do something
;;
4)
do something
;;
5)
mainmenu
;;
6)
uexit # calling the function to exit
exit 1
;;
esac
done
;;
2)
while :
do
submenu2 # calling another menu here
case $subchoice2 in
1)
do something
;;
2)
do something
;;
3)
while :
do
submenu3 # calling another menu here
case $subchoice3 in
1)
do something
;;
2)
do something
;;
3)
do something
;;
4)
do something
;;
5)
submenu2
;;
6)
uexit
exit 1
;;
esac
done
;;
4)
do something
;;
5)
mainmenu
;;
6)
uexit
exit 1
;;
esac
done
;;
3)
do something
;;
4)
uexit
exit 1
;;
esac
return
}
mainmenu
Here's an example using select and the argument array to easily switch between many menus. The same code prints all the menus and reads all the replies using the select builtin.
We use the the $1 variable here to define which menu it's currently showing, which you can alter at will with the set -- and shift commands. You can easily use it like a stack, with set -- nextmenu "$*" [b]pushing a menu to show next and [b]shift popping a menu to return to the previous one.
But set -- will let you set menus in whatever arbitrary order you want, too.
#!/bin/bash
IFS="|" # Split menus on |, so we can have spaces in titles
# Use parameters as a stack. $1 is the current menu.
# we can add with set -- again, or remove with 'shift'.
set -- main
while [ "$#" -gt 0 ]
do
case "$1" in # Look up the right options for the menu
main) OPTIONS="CH1|CH2|CH3|Q" ;;
submenu1) OPTIONS="A|B|C|Q" ;;
submenu2) OPTIONS="D|E|F|Q" ;;
submenu3) OPTIONS="X|Y|Z|Q" ;;
esac
PS3=$'\n'"$1 menu: " # Special variable controlling select prompt
# Print menu, get reply
select X in $OPTIONS
do
[ -z "$X" ] || break
done
# We treat Q specially, it returns to the previous menu.
if [ "$X" = "Q" ]
then
shift # Return to previous menu
continue # Go to top of loop
fi
case "$1" in
main)
case "$X" in
CH1) set -- submenu1 "$*" ;;
CH2) set -- submenu2 "$*" ;;
CH3) set -- submenu3 "$*" ;;
esac
;;
submenu1)
case "$X" in
A) # Switch to submenu2 by removing submenu1 before adding
shift
set -- submenu2 "$*"
;;
B) ;;
C) ;;
esac
;;
submenu2)
case "$X" in
A) ;;
B) ;;
C) ;;
esac
;;
submenu3)
case "$X" in
A) ;;
B) ;;
C) ;;
esac
;;
*) echo "Unknown menu $1, removing"
shift
;;
esac
done
echo "Menu list is empty, quitting" >&2
Sorry.. I should have been more specific... My OS is solaris.. Shell is bash... Everything in my script is a function.. The script itself is over 2000 lines with every "do something" in the code a function...
The submenus are exacly like the main menu that you see except it will only have the menu design and will willl read an input from the user. The processing of the read is done in the submenu.. for example:
Thanks a mil...That looks very interesting.. I will try it as soon as i have access to a terminal.
The only thing is, the number is options in my menus are usually 10 or more and the option is uaually something like "Show logs that belong to the XX service" and such. I have never used select before as well so i am a bit apprehensive. I am excited to try this out at the same time
---------- Post updated at 08:19 PM ---------- Previous update was at 08:10 PM ----------
That is exactly how i have it and it will work fine as long as you do not have another submenu "sub_menu_2" inside inside the function "sub_menu"... In this case, when you call the function "sub_menu_2" and then return back to "sub_menu", any choice you make will take you to "sub_menu_2"... This is because, "sub_menu_2" calls "sub_menu" and when you choose an option , it simply returns back after the call and still in the while loop in "sub_menu_2"
I used IFS="|" so you could have long option names with spaces in there.
You'll probably want to check REPLY instead of X for the value select reads, though! That'll be the number the user inputs, rather than the title displayed.
It may even be possible to have the menus read from file...
I tried the exact code that you posted and it works a charm. But if i modify it as highlighted above, then i am getting the following error.
In addition, if i define the menus your way, they aren't aesthetically goodlooking which is a requirement for the script. I designed the menu function using tput so that the menus will be properly spaced and will be in the centre of the terminal. I tried calling the tput function to align the menu using backticks as below and it got messy to say the least
i=1
j=4
main) OPTIONS="`align $i $j`;Do maintenance|`align $i $j`;`align $i $j`;Look at logs|`align $i $j`;Look at Processes|Q" ;;
# The align function has a bunch of tput commands and increments the value of i and j for each call of the function.
Why are you still selecting based on $X though? Use $REPLY, which will be the number typed in, so you can have cases 1) ;; 2) ;; instead of cramming entire titles into them, padding and all...