Shell programming and simple menus - part 1
The anatomy of a menu shell script
Summary
Learning to build a menu can quickly become an interesting exercise in
shell programming. In this first of two columns on building menus, we
show different ways of displaying prompts, how to define a function,
and how to use case-esac . We then put everything together
in a sample shell script menu and explain it piece by piece.
(2,900 words)
Any programmer or system administrator can
benefit from limiting end-user
access to the things that they actually need. For the timid, Unix offers
little help on how to start, and for the adventurous Unix offers too
many simple commands that can cause havoc.
A menu system is an almost mandatory add-on to any Unix system in the real
world. In saying "real world" Unix system, I mean one that doesn't involve
sitting in a dark room hunched over a glowing terminal trying to determine
the next highest prime number.
Commercial menu systems involve more than a simple prompt that dispatches
to some program based on a user selection. But often a simple approach will
get the job done. While I was creating EZMENU for Unix, one customer
required just such a simple solution.
The menu had to:
- Provide one or more prompts.
- Allow the user to select a prompt.
- Execute one or more commands associated with the selection.
The menu also had to provide a method for the user to exit the menu.
The design of the menu is simple, and it provides an excellent vehicle to
learn some shell programming tricks. The logic for a menu can be described
in the following simple steps:
- Display a menu.
- Get the user's menu pick.
- Check that the pick is valid and if not, display an error message and
return to step 1.
- If the user's menu pick is to exit, then exit.
- Otherwise, execute the command(s) associated with the menu pick.
- Loop back to step 1.
The menu prompts can be displayed directly using the echo command as in:
echo "Please enter 1 to select Accounts Receivable"
or indirectly using a variable as in:
amenu="Please enter 1 to select Accounts Receivable"
echo $amenu
A menu pick can be collected from the user by using the read command which
will read input from the keyboard as in:
echo "Please enter your choice"
read answer
Defining functions
Another important feature of the shell (sh or ksh) is the ability to define
a function. A shell function is a collection of one or more shell commands
that are given a function name. A function can be executed by issuing the
name of the function in the shell script as if it were a Unix command.
However, a function must be defined before it can be executed. To define a
function, give it a name followed by an open and close parentheses,
followed by opening and closing curly braces. Within the curly braces place
the commands to be executed.
The following lines define a function
pagedir and then execute it. The function executes
an ls -l command and then pipes
the results through more to create a paging display.
#!/bin/sh
# Define a paged directory listing function
pagedir () { ls -l|more ; }
# and then execute it
pagedir
Repeating a command or set of commands can be done by enclosing the
commands within a while-do-done command.
In the following example the shell logic loops until the user enters
something other than "y."
#!/bin/sh
answer=y
while [ $answer = y ]
do
echo "Hello"
echo "go again?"
read answer
done
Shell scripts also allow for a case-select type construction, case-esac that executes one of several choices based on a variable containing one of
a list of values. In the example below the choices are a), b), c), and
finally, *), which stands for anything else.
#!/bin/sh
# How to use case-esac
echo "Please enter a letter a through c"
read answer
case $answer in
a) echo You chose a;;
b) echo You chose b;;
c) echo You chose c;;
*) echo You did not chose a, b or c;;
esac
A case-esac choice can also be set up to allow the user to
enter the upper or lower case versions. In the example below the choices
are a|A), b|B), c|C), or *) which stand for a or A (a|A), b or B (b|B),
c or C (c|C), or anything else.
# How to use case-esac with upper or lower case.
echo "Please enter a letter a through c"
read answer
case $answer in
a|A) echo You chose a;;
b|B) echo You chose b;;
c|C) echo You chose c;;
*) echo You did not chose a, b or c;;
esac
This set of tools can be used to build a simple shell script menu such as
that shown in Listing 1 (below). Listing 1 contains line numbers to the left of
each actual line in order to facilitate the explanation of the listing.
This menu allows other programs or shell commands to be launched directly
from the menu. As further items are needed for the menu, a prompt can be
added as emenu , and the program name or commands can be placed inside the
function definition of epick () . The required commands should replace badchoice () as the routines to execute.
I chose .mnu as an extension for this script file to indicate that it was a
menu, but there is no need for the extension.
Listing 1: Sample shell script menu
1. #!/bin/sh
2. # sample.mnu
3. # A simple script menu under Unix
4. # Main logic starts at MAIN LOGIC
5. # The logo will be displayed at the top of the screen
6. LOGO="Sample Menu"
7.
8. #------------------------------------------------------
9. # MENU PROMPTS
10. #------------------------------------------------------
11. # A list of menu prompts to be displayed for the user.
12. # The list can be modified.
13. # In this first list, enter the menu prompt as it should appear
14. # on the screen for each of the letters A - L. In this example
15. # menu pick variables emenu through lmenu are blank as there
16. # are no menu selections for keys E through L.
17.
18. amenu="a. Job Scheduling" ;
19. bmenu="b. Set Standard Defaults " ;
20. cmenu="c. Display Directory Listing " ;
21. dmenu="d Payroll Menu " ;
22. emenu=" " ;
23. fmenu=" " ;
24. gmenu=" " ;
25. hmenu=" " ;
26. imenu=" " ;
27. jmenu=" " ;
28. kmenu=" " ;
29. lmenu=" " ;
30.
31. #------------------------------------------------------
32. # MENU FUNCTION DEFINITIONS
33. #------------------------------------------------------
34.
35. # Define a function for invalid menu picks
36. # The function loads an error message into a variable
37. badchoice () { MSG="Invalid Selection ... Please Try Again" ; }
38.
39. # For each prompt displayed above, there is a list of
40. # commands to execute in response to the user picking the
41. # associated letter.
42. # They are defined as functions
43. # apick () through lpick () where
44. # apick () corresponds to the menu
45. # prompt amenu which is selected
46. # selected by pressing a or A.
47. # bpick () corresponds to the menu
48. # prompt bmenu which is selected by
49. # pressing b or B and so on.
50. # Any menu item that is not
51. # assigned a set of commands, is
52. # assigned
53. # the function badchoice () as a default for that pick.
54. # If the user
55. # selects a menu key that is assigned
56. # to badchoice (). This function
57. # causes an error message to be
58. # displayed on the screen.
59. # To add items to this second
60. # list, replace badchoice ()
61. # with the commands to run when
62. # that letter is pressed.
63. # The following steps simply define
64. # the functions, but do not cause
65. # any shell program steps to be executed.
66.
67. apick () { sched ; }
68. bpick () { defmnt ; }
69. cpick () { ls -l| more ; echo Press Enter ; read DUMMY ; }
70. dpick () { payroll.mnu ; }
71. epick () { badchoice ; }
72. fpick () { badchoice ; }
73. gpick () { badchoice ; }
74. hpick () { badchoice ; }
75. ipick () { badchoice ; }
76. jpick () { badchoice ; }
77. kpick () { badchoice ; }
78. lpick () { badchoice ; }
79.
80. #------------------------------------------------------
81. # DISPLAY FUNCTION DEFINITION
82. #------------------------------------------------------
83. # This function displays the menu.
84. # The routine clears the screen, echoes
85. # the logo and menu prompts
86. # and some additional messages.
87. # Note that this definition does
88. # not cause the function to
89. # be executed yet, it just defines
90. # it ready to be executed.
91.
92. themenu () {
93. # clear the screen
94. clear
95. echo `date`
96. echo
97. echo "\t\t\t" $LOGO
98. echo
99. echo "\t\tPlease Select:"
100. echo
101. echo "\t\t\t" $amenu
102. echo "\t\t\t" $bmenu
103. echo "\t\t\t" $cmenu
104. echo "\t\t\t" $dmenu
105. echo "\t\t\t" $emenu
106. echo "\t\t\t" $fmenu
107. echo "\t\t\t" $gmenu
108. echo "\t\t\t" $hmenu
109. echo "\t\t\t" $imenu
110. echo "\t\t\t" $jmenu
111. echo "\t\t\t" $kmenu
112. echo "\t\t\t" $lmenu
113. echo "\t\t\tx. Exit"
114. echo
115. echo $MSG
116. echo
117. echo Select by pressing the letter and then ENTER ;
118. }
119.
120. #------------------------------------------------------
121. # MAIN LOGIC
122. #------------------------------------------------------
123. # Every thing up to this point has been to define
124. # variables or functions.
125. # The program actually starts running here.
126.
127. # Clear out the error message variable
128. MSG=
129.
130. # Repeat the menu over and over
131. # Steps are:
132. # 1. Display the menu
133. # 2. 'read' a line of input from the key board
134. # 3. Clear the error message
135. # 4. Check the answer for a or A or b or B etc. and dispatch
136. # to the appropriate program or function or exit
137. # 5. If the entry was invalid call the badchoice () function
138. # to initialize MSG to an error message
139. # 6. This error message is used when setting up the menu
140. # for a menu pick that is valid but has no command
141. # associated with it.
142.
143. while true
144. do
145. # 1. display the menu
146. themenu
147.
148. # 2. read a line of input from the keyboard
149. read answer
150.
151. # 3. Clear any error message
152. MSG=
153.
154. # 4. Execute one of the defined functions based on the
155. # letter entered by the user.
156.
157. # 5. If the choice was E through L, the pre-defined
158. # function for that pick will execute badchoice ()
159. # which loads an error message into MSG
160.
161. case $answer in
162. a|A) apick;;
163. b|B) bpick;;
164. c|C) cpick;;
165. d|D) dpick;;
166. e|E) epick;;
167. f|F) fpick;;
168. g|G) gpick;;
169. h|H) hpick;;
170. i|I) ipick;;
171. j|J) jpick;;
172. k|K) kpick;;
173. l|L) lpick;;
174.
175. # If the user selects =91x=92 to exit then break out
176. # of this loop
177. x|X) break;;
178.
179. # 6. If the entry was invalid call the badchoice function
180. # to initialize MSG to an error message
181. *) badchoice;;
182.
183. esac
184.
185. # Do it again until the user enters =91x=92.
186. done
What the menu shell script does
Lines 1 through 30 contain the "data" for the menu system. Line 6 contains
a menu name or logo that is displayed at the top of the menu. Lines 18
through 29 include the variables amenu through lmenu , containing the prompts for 12 lines of menu options. In Listing 1 prompt values are
defined for amenu through dmenu . The variables emenu through lmenu are left
blank. To create your own custom menu you would modify these value to
display up to 12 lines of menu options.
Lines 31 through 119 contain function definitions used by the script. In
all of these lines of code, no actual program steps are performed. Instead
the script builds the functions that will be used in the main logic of the
program.
Line 35 contains a definition for a badchoice () function.
This is a simple
function that is used by the menu system whenever the user:
- Selects an invalid key. A through L, a through l, and X or x are
the only valid keys.
- Selects a valid key that does not have a menu pick associated with it.
The badchoice () function is very simple. It loads a
variable with an error message.
Lines 67 through 78 contain the definitions of 12 functions,
apick () through lpick () .
These functions are the commands to be executed
when a user makes a menu pick. For epick through
lpick , the command is to
call the badchoice () function.
The apick () function at line 67 starts the program, sched, a job
scheduling program.
The bpick () function at line 68 starts defmnt, another program for
maintaining some sort of defaults.
The cpick () function at line 69 invokes multiple commands, ls -l| more followed by echo Press Enter followed by
read DUMMY . These separate commands are all captured in a single line
of semi-colon separated commands as:
{ls -l| more ; echo Press Enter ; read DUMMY ; }
The dpick () function at line 70 runs another menu script titled
payroll.mnu.
These definitions of apick () through lpick ()
can be modified along with the
prompts at lines 18 through 29 to create a custom menu. The remainder of
the menu would be the same for all shell script menus.
At lines 92 through 118 a long function, themenu () , is defined. This
function is used to display the menu on the screen. It clears the screen,
echoes the date, the menu name ($LOGO), the 12 menu picks amenu through
lmenu , an exit prompt, a $MSG variable, and a few other cosmetics to the
screen.
So far nothing has actually happened in the program. To this point it
consists of definitions of variables and functions to be used in the
program.
The program itself appears at lines 120 through 186 at the end of the
script beginning at MAIN LOGIC.
The program is one line of initialization of the MSG variable at line 128 and an infinite loop from lines 143 through 186.
Inside the loop the program steps are:
- Display the menu including all prompts and the MSG variable.
- "Read" an answer as a line of input from the key board.
- Clear the MSG variable.
- Check the answer for a or A through l or L and dispatch to the appropriate program or function.
- Check for x or X and exit.
- If the entry was invalid or for a menu pick that does not exist
because it itself calls
badchoice () , call the badchoice () function to
initialize the MSG variable to an error message.
- Loop forever.
After you have created sample.mnu be sure to make it executable by typing:
chmod a+x sample.mnu
The script displays a simple menu as in Figure 1 and asks the user to enter
a single character and press enter to execute the request.
Figure 1 - The screen produced by sample.mnu
------------------------------------------------------------------------
Fri May 30 14:30:18 PDT 1997
Sample Menu
Please Select:
a. Job Scheduling
b. Set Standard Defaults
c. Display Directory Listing
d Payroll Menu
x. Exit
Invalid Selection Please try again
Select by pressing the letter and then ENTER
------------------------------------------------------------------------
If the user enters an invalid letter or selects a valid letter that has
not yet had a selection attached to it, an Invalid Selection message is
displayed. An example of this message is already on the screen in line 21
as shown in Figure 1.
This provides a simple menu that can be maintained by editing the prompts
at lines 18 through 29 and the picks at lines 67 through 78.
In the next installment we'll take this simple menu concept another few
steps and learn some more about shell programming.
Contact
us for a free consultation. |