Shell programming and simple menus - part 2

RELATED TOPICS

Unix Insider –

In last month's column we started a simple menu system, as useful utility that illustrated several shell programming concepts. Now let's take that a step further. In the next example, the menu system is broken into two parts: the shell script that executes the menu, and a separate "data" file that contains the menu prompts and commands to be executed.

Listing 2 looks very similar to the first menu in last month's Listing 1 (see sidebar) except that it is missing all of the menu prompts and commands to execute! Where are they?

Listing 3 is an example of a menu file to be processed by

<font face="Courier">shellmenu</font>
. This file contains the missing information. You have to study both listings to see how these work together. Both listings include line numbers to simplify the explanations.

If you compare last months Listing 1 to Listing 2 and Listing 3 from this month, you will see that these two listings are almost identical, but the prompts and function definitions have been split out of Listing 2 and placed in Listing 3.

The key points to look at in Listing 2 are "TEST THE COMMAND LINE" at lines 68 through 80, "TEST THE MENU FILE" at lines 81 through 100 and "READ THE MENU FILE" at lines 132 through 138.

Be sure that

<font face="Courier">shellmenu</font>
has been given execute privileges with the command:

<font face="Courier">chmod a+x shellmenu</font>

To start this menu using the file named

<font face="Courier">mf.mnu</font>
shown in Listing 3 as the menu, type the
<font face="Courier">shellmenu</font>
command followed by the name of the file to use as the menu.

<font face="Courier">shellmenu</font>
<font face="Courier">mf.mnu</font>

This causes the value

<font face="Courier">mf.mnu</font>
to be assigned to a variable, $1, inside the shell script. The variables $0, $1, $2, $3 and so on exist in all shell scripts and contain any values that were listed on the command line that was used to start the script. $0 contains the name of the shell script itself,
<font face="Courier">shellmenu</font>
. $1 contains the first word on the command line after
<font face="Courier">shellmenu</font>
, in this case
<font face="Courier">mf.mnu</font>
. The $2 variable would contain the next word or argument on the command line after
<font face="Courier">mf.mnu</font>
if there were any. The shell also creates a variable named $#. This variable contains the number of arguments that were on the command line after the command (in this case,
<font face="Courier">shellmenu</font>
).

For the command:

<font face="Courier">shellmenu</font>
<font face="Courier">mf.mnu</font>
the $# variable should contain the value 1 for the 1 argument on the command line.

In "TEST THE COMMAND LINE" at line 75 through 79, the value of $# is tested, and if it is not 1 then a usage message is displayed and the script exits. The syntax for the test is

<font face="Courier">if [ ! $# = 1 ]
then
    usage
    exit
fi
</font>

Carefully copy the spacing used in the command there are spaces around the left bracket and the right bracket. The "!" means NOT in this test. The test reads "if the number of arguments is not 1 then execute a

<font face="Courier">usage()</font>
function and exit the script."

The

<font face="Courier">usage()</font>
function is defined at lines 10 through 20 and attempts to describe the correct usage for the
<font face="Courier">shellmenu</font>
script.

In "TEST THE MENU FILE", at line 87, a new variable, $MENU, is created and is assigned the value of $1. Another test is done to determine if the file exists and is readable. The test:

<font face="Courier">if [ -r $MENU ]</font>

uses the name of the file in $MENU,

<font face="Courier">mf.mnu,</font>
and then tests two conditions. The -r tests that the file exists and that it is readable. If so it returns "true" logically. We want the opposite test, so a NOT (!) is added in:

<font face="Courier">if [ ! -r $MENU ]</font>

Again notice spaces around the left and right brackets.

This tests if the file

<font face="Courier">mf.mnu</font>
exists and is readable, provides a message for the user, and displays the usage information and exits.

Finally the trick as to how the this menu system works appears in "READ THE MENU FILE" at line 87. Note the period in the command:

<font face="Courier">. $MENU </font>

The period is a special character in a shell script that basically says read the following named file (or the file named in the variable) on this line as if it were part of this current script. It acts very much like a "include" or "copy" directive. As the file is included, the commands in it are executed.

Listing 2: Sample shell script menu driver

1. #!/bin/sh
2. # shellmenu
3. # Simple shell script menu driver under UNIX
4. # Main logic starts at MAIN LOGIC
5.  
6. #---------------------------------------
7. # FUNCTION DEFINITIONS
8. #---------------------------------------
9.  
10. # A function for usage info
11. usage () {
12. echo "Usage: shellmenu menu_file"
13. echo "  A menu file must contain 12 variables"
14. echo "  amenu through l menu"
15. echo "  defining the prompts for a menu"
16. echo "  and the definitions for 12"
17. echo "  functions apick() through lpick()"
18. echo "  that define the commands to be"
19. echo "  executed for each of the 12 picks"
20. }
21. # Define a function for invalid menu picks
22. # The function loads an error message into a variable
23. badchoice ()
24. {
25. MSG="Invalid Selection ... Please Try Again" ;
26. }
27. 
28. #---------------------------------------
29. # DISPLAY FUNCTION DEFINITION
30. #---------------------------------------
31. # This function displays the menu.
32. # The routine clears the screen, echoes
33. # the logo and menu prompts
34. # and some additional messages.
35. # Note that this definition does
36. # not cause the function to
37. # be executed yet, it just defines
38. # it ready to be executed.
39.  
40. displaymenu () {
41. # clear the screen
42. clear
43. echo `date`
44. echo
45. echo "\t\t\t" $LOGO
46. echo
47. echo "\t\tPlease Select:"
48. echo
49. echo "\t\t\t" $amenu
50. echo "\t\t\t" $bmenu
51. echo "\t\t\t" $cmenu
52. echo "\t\t\t" $dmenu
53. echo "\t\t\t" $emenu
54. echo "\t\t\t" $fmenu
55. echo "\t\t\t" $gmenu
56. echo "\t\t\t" $hmenu
57. echo "\t\t\t" $imenu
58. echo "\t\t\t" $jmenu
59. echo "\t\t\t" $kmenu
60. echo "\t\t\t" $lmenu
61. echo "\t\t\tx. Exit"
62. echo
63. echo $MSG
64. echo
65. echo Select by pressing the letter and then ENTER ;
66. }
67.  
68. #---------------------------------------
69. # TEST THE COMMAND LINE
70. #---------------------------------------
71. # There should only be one argument
72. # on the command line, which 
73. # should be the name of a menu file
74.  
75. if [ ! $# = 1 ]
76. then
77.     usage
78.     exit
79. fi
80.  
81. #---------------------------------------
82. # TEST THE MENU FILE
83. #---------------------------------------
84. # Assign the value of the first argument
85. # on the command line,
86.  
87. MENU=$1
88.  
89. # Test that the file exists and is readable.
90. # Abort if it is not
91.  
92. if [ ! -r $MENU ]
93. then
94.     echo "Can't locate or read " $MENU
95.     echo "Press ENTER to acknowledge."
96.     read $answer
97.     usage
98.     exit
99. fi
100.  
101. #---------------------------------------
102. # MAIN LOGIC
103. #---------------------------------------
104. # Clear out the error message
105. MSG=
106.  
107. #---------------------------------------
108. # MAIN LOOP
109. #---------------------------------------
110. # We're here because we have a file
111. # that can be read, so OK to
112. # continue with the main loop of the menu.
113.  
114. # Repeat the menu over and over
115. # Steps are:
116. # 1. Display the menu
117. # 2. 'read' a line of input from the keyboard
118. # 3. Clear the error message
119. # 4. Check the answer for a or A or b or B, etc. and dispatch
120. #    to the appropriate program or function or exit
121. # 5. If the entry was invalid call the
122. #     badchoice() function
123. #    to initialize MSG to an error message
124. # 6. This error message is used when setting
125. #    up the menu for a menu pick that is
126. #    valid but has no command
127. #    associated with it.
128. 
129. while  true
130. do
131.  
132. #---------------------------------------
133. # READ THE MENU FILE
134. # define the menu by reading in the file
135. # named on the command line
136. #---------------------------------------
137.     . $MENU
138.  
139. #---------------------------------------
140. # DISPLAY THE MENU
141. # and read the user pick
142. #---------------------------------------
143.     displaymenu
144.     read answer
145.     MSG=
146. # dispatch to the users selection
147.     case $answer in
148.         a|A) apick;;
149.         b|B) bpick;;
150.         c|C) cpick;;
151.         d|D) dpick;;
152.         e|E) epick;;
153.         f|F) fpick;;
154.         g|G) gpick;;
155.         h|H) hpick;;
156.         i|I) ipick;;
157.         j|J) jpick;;
158.         k|K) kpick;;
159.         l|L) lpick;;
160.         x|X) break;;
161.         *) badchoice;;
162.     esac
163. done
164.  <<<
<font face="Courier">End of Listing</font>
>>>

Listing 3:

<font face="Courier">mf.mnu</font>
-- a file of menu prompts and picks

1. # mf.mnu a simple script menu for shellmenu
2. # This file is read directly into shellmenu
3. # using the dot (.) prefix as in
4. #       
<font face="Courier">shellmenu</font>
this_file 5. # 6. # causing the contents 7. # of this file to be read as part of the shell script 8. 9. # The logo for this menu will be displayed 10. # at the top of the screen 11. LOGO="More Functions" 12. 13. # Here are the list of menu prompts and the 14. # commands that go with them 15. # In the first list, enter the menu prompt 16. # as it should appear 17. # on the menu for each of the letters A - L 18. # In the second list, replace badchoice 19. # with any commands to run 20. # when that letter is pressed. 21. 22. #--------------------------------------- 23. # MENU PROMPTS 24. #--------------------------------------- 25. amenu="a. Job Scheduling" ; 26. bmenu="b. Set Standard Defaults " ; 27. cmenu="c. Display Directory Listing " ; 28. dmenu="d Other Functions" ; 29. emenu=" " ; 30. fmenu=" " ; 31. gmenu=" " ; 32. hmenu=" " ; 33. imenu=" " ; 34. jmenu=" " ; 35. kmenu=" " ; 36. lmenu=" " ; 37. 38. #--------------------------------------- 39. # MENU ACTIONS AND COMMANDS 40. #--------------------------------------- 41. apick () { shmnu ; } 42. bpick () { defmnt ; } 43. cpick () 44. { 45. ls -l| more ; echo Press Enter ; read DUMMY ; 46. } 47. dpick () {
<font face="Courier">shellmenu</font>
sf.mnu ; } 48. epick () { badchoice ; } 49. fpick () { badchoice ; } 50. gpick () { badchoice ; } 51. hpick () { badchoice ; } 52. ipick () { badchoice ; } 53. jpick () { badchoice ; } 54. kpick () { badchoice ; } 55. lpick () { badchoice ; } 56. <<<
<font face="Courier">End of Listing</font>
>>>

Putting it all together

Listing 4 is a logical representation of combination of

<font face="Courier">shellmenu</font>
including the
<font face="Courier">. $MENU</font>
command. Once you look at the combination, there is little difference between the original Listing 1 last month and Listing 4.

Listing 4: The logical combination of

<font face="Courier">shellmenu</font>
and
<font face="Courier">mf.mnu</font>

RELATED TOPICS
1 2 Page
Top 10 Hot Internet of Things Startups
View Comments
You Might Like
Join the discussion
Be the first to comment on this article. Our Commenting Policies