Programming Bash #7: Bash Program Needs Help

0

Last Updated on December 29, 2023 by David Both

So far in this series we created our first, very small, one-line Bash script. We also explored the reasons for creating shell scripts and why they are the most efficient option for the system administrator, rather than compiled programs. We than began the task of creating a simple template that we can use as a starting point for other Bash programs.

In this installment we will learn how to add a simple help function to our template. In order to create our help facility we will also learn about using functions and how to handle command line options such as -h.

Why help?

I find that even fairly simple Bash programs should have some sort of help facility even if it is fairly rudimentary. Many of the Bash shell programs I write are used infrequently enough that I may forget the exact syntax of the command I need to issue. Some are just so complex that I need to review the options and arguments needed even though I use them frequently.

Having a built-in help function allows us to view those things without needing to resort to inspecting the code itself. A good and complete help facility is also one part of program documentation.

About functions

Shell functions are lists of Bash program statements that are stored in the shell’s environment and which can be executed like any other command by typing its name at the command line. Shell functions may also be known as procedures or subroutines depending upon which other programming language you might be using.

Functions are called in our scripts or from the CLI by using their names, just as we would for any other command. In a CLI program or a script, the commands in the function are executed when called and then the sequence of program flow returns to the calling entity and the next series of program statements in that entity is executed.

The syntax of a function is simple:

FunctionName(){program statements}

If you are not already there, make ~/testdir6 the PWD. Refer to the Preparation section of the article, Programming Bash: Logical Operators.

Let’s create a simple function at the CLI. The function is stored in the shell environment for the shell instance in which it is created. We are going to create a function called “hw” which stands for hello world. Enter the following code at the CLI and press Enter. Then enter hw as you would any other shell command.

[dboth@testvm1 testdir6]$ hw(){ echo "Hi there kiddo"; } 
[dboth@testvm1 testdir6]$ hw
Hi there kiddo
[dboth@testvm1 testdir6]$ 

Ok, so I am a little tired of the standard “Hello world” we usually start with. Now let’s list all of the currently defined functions. When called from the command line or within a program, a function performs its programmed task and then exits, returning control to the calling entity, the command line or the next Bash program statement in a script after the calling statement. There are a lot of them so I have shown just the new hw function which appears very near the end of the data stream.

[dboth@testvm1 testdir6]$ declare -f | less
<snip>
hw () 
{ 
    echo "Hi there kiddo"
}

Now let’s remove that function because we do not need it any more. We can do that with the unset command.

[dboth@testvm1 testdir6]$ unset -f hw ; hw
-bash: hw: command not found
[dboth@testvm1 testdir6]$

Creating the Help function

Open the hello program in an editor and add the help function shown below to the code of the hello program. Place the help function after the copyright statement but before the echo “Hello world!” statement. This help function will display a short description of the program, a syntax diagram, and a short description of each of the available options. We also add a call to the Help function to test it, and some comment lines that provide a visual demarcation between the functions and the main portion of the program.

################################################################################
# Help                                                                         #
################################################################################
Help()
{
   # Display Help
   echo "Add description of the script functions here."
   echo
   echo "Syntax: scriptTemplate [-g|h|v|V]"
   echo "options:"
   echo "g     Print the GPL license notification."
   echo "h     Print this Help."
   echo "v     Verbose mode."
   echo "V     Print software version and exit."
   echo
}

################################################################################
################################################################################
# Main program                                                                 #
################################################################################
################################################################################

Help
echo "Hello world!"

The options described in this help function are some that might be typical in the programs I write, although none are yet present on the code. Run the program to test it.

[dboth@testvm1 testdir6]$ ./BashTemplate.sh 
Add description of the script functions here.

Syntax: scriptTemplate [-g|h|v|V]
options:
g     Print the GPL license notification.
h     Print this Help.
v     Verbose mode.
V     Print software version and exit.

Hello World!
[dboth@testvm1 testdir6]$

Because we have not added any logic to only display the help when we need it, the program will always display the help. Now we do know, however, that our function is working correctly so we can add some logic to only display the help when we use a -h option at the command line invocation of the program.

Handling options

The ability of a Bash script to handle command line options such as -h gives us some powerful capabilities to direct the program and modify what it does. In the case of our -h option, we want the program to print the help text to the terminal session and then quit without running the rest of the program. The ability to process options entered at the command line can be added to our Bash script using the while command, which we have encountered in one of my previous articles, Programming Bash: Loops, in conjunction with the getops and case commands.

The getops command reads any and all options specified at the command line and creates a list of those options. The while command loops through the list of options by setting the variable $options for each in the code below. The case statement is used to evaluate each option in turn and execute the statements in the corresponding stanza. The while statement will continue to evaluate the list of options until they have all been processed or an exit statement is encountered which terminates the program.

Be sure to delete the Help function call just before the echo "Hello world!" statement so that the main body of the program now looks like this.

################################################################################
################################################################################
# Main program                                                                 #
################################################################################
################################################################################
# Process the input options. Add options as needed.                            #
################################################################################
# Get the options
while getopts ":h" option; do
   case $option in
      h) # display Help
         Help
         exit;;
   esac
done

echo "Hello world!"

Notice the explicit double semicolon at the end of the exit statement in the case option for -h. This is required for each option we will add to this case statement to delineate the end of each option.

Testing

Our testing is now a little more complex. We need to test our program with a number of different options – and no options – to see how it responds. First, let’s test to ensure that with no options to ensure that it prints “Hello world!” as it should.

[dboth@testvm1 testdir6]$ ./BashTemplate.sh 
Hello World!

That works so let’s now test the logic that displays the help text.

[dboth@testvm1 testdir6]$ ./BashTemplate.sh -h
Add description of the script functions here.

Syntax: scriptTemplate [-g|h|v|V]
options:
g     Print the GPL license notification.
h     Print this Help.
v     Verbose mode.
V     Print software version and exit.

[dboth@testvm1 testdir6]$

That works as we expect it to so let’s now try some testing to see what happens when we enter some unexpected options.

[dboth@testvm1 testdir6]$ ./hello -x
Hello world!
[dboth@testvm1 testdir6]$ ./hello -q
Hello world!
[dboth@testvm1 testdir6]$ ./hello -lkjsahdf
Add description of the script functions here.

Syntax: scriptTemplate [-g|h|t|v|V]
options:
g     Print the GPL license notification.
h     Print this Help.
v     Verbose mode.
V     Print software version and exit.

[dboth@testvm1 testdir6]$

Our program just ignores the options for which we have not created specific responses without generating any errors. Although if you noticed the last entry with -lkjsahdf for options, because there is an “h” in the list of options, our program did recognize that and print the help text. Our testing has shown that one thing that is missing is the ability to handle incorrect input and terminate the program if any is detected.

We can add another case stanza to the case statement that will match any option for which there is no explicit match. This general case will match anything we have not provided a specific match for. Our case statement now looks like this with the catch-all match of \? as the last case. Any additional specific cases must precede this final one.

while getopts ":h" option; do
   case $option in
      h) # display Help
         Help
         exit;;
     \?) # incorrect option
         echo "Error: Invalid option"
         exit;;
   esac
done

Test the program again using the same options as before and see how this works now. Notice that any invalid options at all cause the program to terminate even if the -h option is present.

Where we are

We have accomplished a good amount in this session by adding the capability to process command line options and a help procedure. Our Bash script now looks like this.

#!/usr/bin/bash
################################################################################
#                              scriptTemplate                                  #
#                                                                              #
# Use this template as the beginning of a new program. Place a short           #
# description of the script here.                                              #
#                                                                              #
# Change History                                                               #
# 11/11/2023  David Both    Original code. This is a template for creating     #
#                           new Bash shell scripts.                            #
#                           Add new history entries as needed.                 #
#                                                                              #
#                                                                              #
################################################################################
################################################################################
################################################################################
#                                                                              #
#  Copyright (C) 2007, 2023 David Both                                         #
#  LinuxGeek46@both.org                                                        #
#                                                                              #
#  This program is free software; you can redistribute it and/or modify        #
#  it under the terms of the GNU General Public License as published by        #
#  the Free Software Foundation; either version 2 of the License, or           #
#  (at your option) any later version.                                         #
#                                                                              #
#  This program is distributed in the hope that it will be useful,             #
#  but WITHOUT ANY WARRANTY; without even the implied warranty of              #
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
#  GNU General Public License for more details.                                #
#                                                                              #
#  You should have received a copy of the GNU General Public License           #
#  along with this program; if not, write to the Free Software                 #
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA   #
#                                                                              #
################################################################################
################################################################################

################################################################################
# Help                                                                         #
################################################################################
Help()
{
   # Display Help
   echo "Add description of the script functions here."
   echo
   echo "Syntax: scriptTemplate [-g|h|t|v|V]"
   echo "options:"
   echo "g     Print the GPL license notification."
   echo "h     Print this Help."
   echo "v     Verbose mode."
   echo "V     Print software version and exit."
   echo
}

################################################################################
################################################################################
# Main program                                                                 #
################################################################################
################################################################################
################################################################################
# Process the input options. Add options as needed.                            #
################################################################################
# Get the options
while getopts ":h" option; do
   case $option in
      h) # display Help
         Help
         exit;;
     \?) # incorrect option
         echo "Error: Invalid option"
         exit;;
   esac
done

echo "Hello world!"

Be sure to test this version of our program thoroughly. Use random input and see what happens. You should also try testing valid and invalid options without using the dash (-) in front.

Next time

In this session we have added a help function as well as the ability to process command line options in order to selectively display the help. Our program is getting a little more complex and so testing is becoming more important and requires more test paths in order to be complete.

In the next article we will look at initializing variables and doing a bit of sanity checking so that we can ensure that the program is running under the correct set of conditions.


Series Articles

This list contains links to all eight articles in this series about Bash.

  1. Programming Bash #1 – Introducing a New Series
  2. Programming Bash #2: Getting Started
  3. Programming Bash #3: Logical Operators
  4. Programming Bash #4: Using Loops
  5. Programming Bash #5: Automation with Scripts
  6. Programming Bash #6: Creating a template
  7. Programming Bash #7: Bash Program Needs Help
  8. Programming Bash #8: Initialization and sanity testing