Quick lessons on shell programming

Unix Insider –

The Unix shell in its various incarnations, the Bourne Shell (

<font face="Courier">sh</font>
), the Korn Shell (
<font face="Courier">ksh</font>
), the C Shell (
<font face="Courier">csh</font>
), the Bourne Again Shell (
<font face="Courier">bash</font>
), is a powerful programming tool.

Although most people are familiar with the ability of the shell to interpret commands as they are typed in at the terminal, many are not familiar with its ability to execute programs or shell scripts. Even more people are not familiar with all of the tools available in a shell script.

The tools that can be used to create a shell script program are all built into the shell -- whichever one you are using -- but they are rarely used in single command lines that are typed in at the terminal.

Functions are a powerful feature of shell programming for the the Bourne Shell and its derivatives, Korn Shell and

<font face="Courier">bash</font>
.

For these exercises type

<font face="Courier">ksh</font>
to start the Korn shell and press Enter. If an error occurs then type
<font face="Courier">sh</font>
to start the Bourne shell and press Enter.

A function is a single command that can be defined to stand in for one or more Unix commands. Using a function involves two steps: defining the function and then invoking or executing the function.

The first safe step to take is to check if the function name that you intend to use is currently in use. In this case you are going to create a function named "greetings." Type greetings and press Enter. You should receive a message that greetings could not be found. If this is not the case, then select another command such as "hi_there" until you find one that is not found:

<font face="Courier">greetings
ksh: greetings: not found 
</font>

To define a function give the function a standard Unix-type name, in this case greetings, followed by the definition of the function. There are two different versions of the syntax for defining a function. In the example below, either command will define a function named greetings that prints "Hello there" on the screen:

<font face="Courier">$ function greetings { echo Hello there ; }
$ greetings () { echo Hello there ; }</font>

Type in either command, and you will notice that nothing appears to happen. All you have done is define the function, but you have not yet executed it. To execute the function, type its name just as you would a standard Unix command. The function will execute:

<font face="Courier">$ greetings
Hello there
$
</font>

Expand on this a bit and define the function to include displaying the environment variable containing the user I.D.:

<font face="Courier">$ greetings () { echo Hello there $LOGNAME ; } </font>

Now when you type greetings, the message is more personalized.

<font face="Courier">$ greetings
Hello there mjb
$
</font>

A function may be defined to execute multiple commands by separating the commands with a semi-colon or a newline. The examples below both define a function named "whatdo" that will list processes started by the current user. The

<font face="Courier">ps -ef</font>
command is piped in
<font face="Courier">grep</font>
to search for lines containing the user id ($LOGNAME) and the result is piped through
<font face="Courier">more</font>
. At the end of the display "Press Enter" is displayed and a dummy variable is read from the keyboard until the user presses Enter. These examples use the two different syntaxes for defining a function. Note that in the second version, a newline is used to replace each semi-colon, and after each new line the continuation prompt (>) is displayed:

<font face="Courier">$ whatdo () { ps -ef|grep $LOGNAME|more ; echo Press Enter ; read x ; }
</font>

or

<font face="Courier">$ function whatdo {
> ps -ef|grep $LOGNAME|more
> echo Press Enter
> read x
> }
$
</font>

Now type

<font face="Courier">whatdo</font>
, and you will see a display of your activities:

<font face="Courier">$ whatdo
   mjb   260     1  7  Jul 31   01      0:04 -ksh
   mjb   261     1  0  Jul 31   02      0:01 -ksh
   mjb  2293     1  0  10:31:37 03      0:01 -ksh
   mjb  2313   260  7  10:45:06 01      0:00  ps -ef
   mjb  2314   260  7  10:45:06 01      0:00  grep mjb
   mjb  2315   260  7  10:45:06 01      0:00  more
Press Enter
$
</font>

Integrating functions into shell scripts

While functions are fine on the command line, their true power comes about in shell scripts.

First some rules about shell scripts. Ensure that any shell script you create will run using

<font face="Courier">/bin/sh</font>
or
<font face="Courier">/bin/ksh</font>
by starting the shell script in one of two ways:

  1. Always start with a blank line.
  2. Always start with the line
    <font face="Courier">#! /bin/sh</font>
    or
    <font face="Courier"> #!
    /bin/ksh </font>

The first method will work on older versions of Unix. The second method has been supported on many versions of Unix for several years now.

In the following examples, I will use the second version.

In the examples I have shown you so far, functions are used to create a sort of alias for one or more other commands. Within shell scripts, functions are used to tidy up the script so that it is more readable and to avoid having to repeat the same logic over and over. It frequently makes the logic of a long shell script easier to follow.

In Listing 1 I have added line numbers to simplify the explanation. You can type this in as

<font face="Courier">simpmenu</font>
using vi or some other editor. Then change the execution status with
<font face="Courier">chmod a+x
simpmenu</font>
.

At lines 4 through 20 a long function named

<font face="Courier">amenu()</font>
is defined. This function clears the screen and displays a set of prompts in a menu format. Lines 24 through 27 define a
<font face="Courier">PressEnter()</font>
function that asks the user to press ENTER and then waits for a key press.

Lines 29 through 44 define functions that are to be used or called in response to each menu pick. Note that each of these three functions uses a call to

<font face="Courier">PressEnter</font>
within the body of the function. This ability to use a function inside another function makes programming much easier. Without this ability, the logic of the
<font face="Courier">PressEnter</font>
function would have to be fully typed in each time within the body of the three main functions that are executed by the menu.

Up until line 63, no actual program has been run. If the shell script stopped at this point, nothing would have happened. At line 63 the program begins with a permanent loop (while true) that runs to line 85. The menu is displayed by calling the function

<font face="Courier">amenu</font>
at line 66. The read command is used to read the variable answer at line 69. At line 74, a case statement tests the value in
<font face="Courier">$answer</font>
and executes one of the three defined functions based on the user's choice or issues a break command that breaks out of the permanent loop.

Listing 1: A simple demonstration of functions

<font face="Courier">1. #! /bin/ksh
2. # simpmenu, A very simple menu
3. 
4. # amenu() defines a function to display a simple menu
5. amenu () {
6. # clear the screen
7. clear
8. echo `date`
9. echo
10. echo "\t\t\tMy Personal Menu"
11. echo
12. echo "\t\tPlease Select:"
13. echo
14. echo "\t\t\t 1. Directory display"
15. echo "\t\t\t 2. Current Activity"
16. echo "\t\t\t 3. Who is logged on"
17. echo "\t\t\t"
18. echo "\t\t\t 0. Exit"
19. echo Select by pressing a number and then ENTER ;
20. }
21. 
22. # A function that asks the user to press enter
23. # and waits for the ENTER Key
24. PressEnter () {
25. echo Press Enter
26. read x
27. }
28. 
29. # A function for each of the menu picks
30. DirectoryDisplay () {
31. ls -l|more
32. PressEnter
33. }
34. 
35. CurrentActivity ()
36. {
37. ps -ef|more
38. PressEnter
39. }
40. 
41. WhoIsLoggedOn () {
42. who|more
43. PressEnter
44. }
45. 
46. # The main logic for the program displays the menu
47. # gets the users entry and initiates the activity
48. #------------------------------------------------------
49. # MAIN LOGIC
50. #------------------------------------------------------
51. # Every thing up to this point has
52. # just defined functions
53. # The program actually starts running here.
54. 
55. # Repeat the menu over and over
56. # Steps are:
57. # 1. Display the menu
58. # 2. 'read' a line of input from the key board
59. # 3. Check the answer for 1, 2, 3 or 0 and dispatch
60. #    to the appropriate function or exit
61. # 4  Repeat until 0 is entered
62. 
63. while  true
64. do
65. # 1. display the menu
66.   amenu
67. 
68. # 2. read a line of input from the keyboard
69.   read answer
70. 
71. # 3. Execute one of the defined functions based on the
72. #    number entered by the user.
73. 
74.   case $answer in
75.       1) DirectoryDisplay ;;
76.       2) CurrentActivity ;;
77.       3) WhoIsLoggedOn ;;
78. 
79. #      If the user selects 0 to exit then break out
80. #      of this loop
81.       0) break ;;
82.   esac
83. 
84. #     Do it again until the user enters 0.
85. done
86. 
87. # Clear the screen on the way out
88. clear
</font>

Listing 2 is the same activity but without functions. It is much harder to read and contains a lot of duplicate code.

Listing 2: The non function version of listing 1

<font face="Courier">1. #! /bin/ksh
2. # simpmenu, A very simple menu
3. 
4. 
5. # The main logic for the program displays the menu
6. # gets the users entry and initiates the activity
7. #------------------------------------------------------
8. # MAIN LOGIC
9. #------------------------------------------------------
10. 
11. # Repeat the menu over and over
12. # Steps are:
13. # 1. Display the menu
14. # 2. 'read' a line of input from the key board
15. # 3. Check the answer for 1, 2, 3 or 0 and dispatch
16. #    to the appropriate function or exit
17. # 4  Repeat until 0 is entered
18. 
19. while  true
20. do
21. # 1. display the menu
22. # clear the screen
23. clear
24. echo `date`
25. echo
26. echo "\t\t\tMy personal menu"
27. echo
28. echo "\t\tPlease Select:"
29. echo
30. echo "\t\t\t 1. Directory display"
31. echo "\t\t\t 2. Current Activity"
32. echo "\t\t\t 3. Who is logged on"
33. echo "\t\t\t"
34. echo "\t\t\t 0. Exit"
35. echo Select by pressing a number and then ENTER 
36. 
37. # 2. read a line of input from the keyboard
38.   read answer
39. 
40. # 3. Execute commands based on the
41. #    number entered by the user.
42. 
43.   case $answer in
44.       1) 
45.             ls -l|more
46.             echo Press Enter
47.             read x ;;
48.       2) 
49.             ps -ef|more
50.             echo Press Enter
51.             read x ;;
52.       3) 
53.             who|more
54.             echo Press Enter
55.             read x ;;
56. 
57. #      If the user selects 0 to exit then break out
58. #      of this loop
59.       0) break ;;
60.   esac
61. 
62. #     Do it again until the user enters 0.
63. done
64. 
65. # Clear the screen on the way out
66. clear
67. 
</font>

Use functions to tidy up long shell scripts and you will be taking advantage of one of the powerful features of the shell programming language.

From CIO: 8 Free Online Courses to Grow Your Tech Skills
Join the discussion
Be the first to comment on this article. Our Commenting Policies