Shell programming and simple menus - part 1

Unix Insider –

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:

<font face="Courier">echo "Please enter 1 to select Accounts Receivable"</font>

or indirectly using a variable as in:

<font face="Courier">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:

<font face="Courier">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

<font face="Courier">pagedir</font>
and then execute it. The function executes an
<font face="Courier"> ls -l</font>
command and then pipes the results through
<font face="Courier">more</font>
to create a paging display.

<font face="Courier">#!/bin/sh
# 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."

<font face="Courier">#!/bin/sh
while [ $answer = y ]
    echo "Hello"
    echo "go again?"
    read answer

Shell scripts also allow for a case-select type construction,

<font face="Courier">case-esac</font>
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.

<font face="Courier">#!/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;;


<font face="Courier">case-esac</font>
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.

<font face="Courier"># 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

<font face="Courier">emenu</font>
, and the program name or commands can be placed inside the function definition of
<font face="Courier">epick ()</font>
. The required commands should replace
<font face="Courier"> badchoice ()</font>
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

<font face="Courier">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

<font face="Courier">amenu </font>
<font face="Courier">lmenu </font>
, containing the prompts for 12 lines of menu options. In Listing 1 prompt values are defined for
<font face="Courier">amenu</font>
<font face="Courier">dmenu</font>
. The variables
<font face="Courier"> emenu</font>
<font face="Courier">lmenu</font>
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

<font face="Courier">badchoice ()</font>
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.


<font face="Courier">badchoice ()</font>
function is very simple. It loads a variable with an error message.

Lines 67 through 78 contain the definitions of 12 functions,

<font face="Courier">apick () </font>
<font face="Courier">lpick ()</font>
. These functions are the commands to be executed when a user makes a menu pick. For
<font face="Courier">epick</font>
<font face="Courier">lpick</font>
, the command is to call the
<font face="Courier">badchoice ()</font>


<font face="Courier">apick ()</font>
function at line 67 starts the program, sched, a job scheduling program.

1 2 Page 1
Page 1 of 2
ITWorld DealPost: The best in tech deals and discounts.
Shop Tech Products at Amazon