Unix 101 by Mo Budlong

Shell programming and simple menus - part 1

The anatomy of a menu shell script

July  1997
[Next story]
[Table of Contents]
Sun's Site

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)

Mail this
article to
a friend
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:

  1. Provide one or more prompts.
  2. Allow the user to select a prompt.
  3. 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:

  1. Display a menu.
  2. Get the user's menu pick.
  3. Check that the pick is valid and if not, display an error message and return to step 1.
  4. If the user's menu pick is to exit, then exit.
  5. Otherwise, execute the command(s) associated with the menu pick.
  6. 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.

# Define a paged directory listing function
pagedir () { ls -l|more ; }
# and then execute it

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."

while [ $answer = y ]
    echo "Hello"
    echo "go again?"
    read answer

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.

# 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;;

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;;

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"
8.  #------------------------------------------------------
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.
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=" "                                 ;
31. #------------------------------------------------------
33. #------------------------------------------------------
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" ; } 
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.
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 ; }
80. #------------------------------------------------------
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.
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. }
120. #------------------------------------------------------
122. #------------------------------------------------------
123. # Every thing up to this point has been to define
124. # variables or functions.
125. # The program actually starts running here.
127. # Clear out the error message variable
128. MSG=
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.
143. while  true
144. do
145. # 1. display the menu
146.   themenu
148. # 2. read a line of input from the keyboard
149.   read answer
151. # 3. Clear any error message
152.   MSG=
154. # 4. Execute one of the defined functions based on the
155. #    letter entered by the user.
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  
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;;
175. #      If the user selects =91x=92 to exit then break out
176. #      of this loop
177.       x|X) break;;
179. # 6. If the entry was invalid call the badchoice function
180. #    to initialize MSG to an error message
181.         *) badchoice;;
183.   esac
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:

  1. Selects an invalid key. A through L, a through l, and X or x are the only valid keys.
  2. 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:

  1. Display the menu including all prompts and the MSG variable.
  2. "Read" an answer as a line of input from the key board.
  3. Clear the MSG variable.
  4. Check the answer for a or A through l or L and dispatch to the appropriate program or function.
  5. Check for x or X and exit.
  6. 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.
  7. 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.


About the author
Mo Budlong is president of King Computer Services, Inc. and has been involved in Unix development on Sun and other platforms for over 15 years. King Computer Services, Inc. specializes in Unix and Client/Server consulting and training and currently publishes the COBOL Just In Time Course, a crash COBOL course to train staff for the Year 2000 problem. Reach Mo at mo.budlong@sunworld.com.

What did you think of this article?
-Very worth reading
-Worth reading
-Not worth reading
-Too long
-Just right
-Too short
-Too technical
-Just right
-Not technical enough

[Table of Contents]
Sun's Site
[Next story]
Sun's Site

[(c) Copyright  Web Publishing Inc., and IDG Communication company]

If you have technical problems with this magazine, contact webmaster@sunworld.com

URL: http://www.sunworld.com/swol-07-1997/swol-07-unix101.html
Last modified: