Using Case Statements in the Shell


Working with the case logic structure...

Some programmers think that the case statement is a useless construct that should have never been implemented, but others can't believe that some languages don't even include it in their command sets.

No matter what the outcome of this "debate," case logic can be very useful to script writers and programmers who like to see choices sequentially laid out before them, even though it might appear to be too hard-coded or inflexible at times.

The case structure when wrapped by a loop structure can provide a comprehensive way of dealing with a wide variety of situations, or cases, that need to be processed. The example below is a working script that accepts a list of devices and sets parameters on each one in turn as it progresses through the loop and is "caught" by a case match.




do_kissparms: a scripting "case" in point...

Let's take a moment to preview some of the highlights for this script. We will abstract the "bare bones" so that the method becomes clearer. Our example uses a while/do/done looping structure, a little different from the for/do/done . It loops until the $1 variable is empty or null. The case looks for "matches" and when it finds one, it executes all the instructions until it reaches the ";;" characters, and then it exits the case. However, being nested in a loop, the case continues "searching" until it has nothing left to process...

The Loop and Case Logic Structure
...

#----- The outer while loop, test to see if something in $1
while [ "$1" ]
do

#----- The case statements
        case $1 in
        case1_match)
                command(s) for case1
        ;;   
        case2_match)
                command(s) for case2
        ;;
        *)
                no match, so default case
        ;;
        esac

#----- Push next item (device) into $1 variable.
shift
done

...

And here is the actual coding in detail:
#! /bin/sh
# do_kissparms
# 03-26-98
# 04-29-00
# 06-25-04
# KA1FSB/kbn
# ---------------------------------------------------------------------
# Use this script to change the 'behavior' of the radio for TX/RX, etc
#
# Usage: do_kissparms 
#  $1 is the port name passed in ... Enter as many devices as you like,
#  and are listed below.
#
# For AX25 Utilities, version 2.1.42a under /kiss directory
# ---------------------------------------------------------------------

#----- Parameter list
# port:          -p     # name of port from axports file
# full duplex:   -f y|n      # yes is for full
# txtail:        -l          # in milliseconds
# persistence:   -r          # 0-255
# slottime       -s          # in milliseconds
# txdelay        -t          # in milliseconds
# normal mode    -x          # optional: may not always work


#----- If operating half duplex, the resptime timer is suspended when PTT
#-----  or carrier detect is operating.


while [ "$1" ]
do
#----- Should have one for each port!
        case $1 in
        hf)
                /usr/sbin/kissparms -p $1 -f n -l 300 -r 192 -s 50 -t 300
        ;;
        hf1)
                /usr/sbin/kissparms -p $1 -f n -l 300 -r 192 -s 50 -t 300
        ;;
        vhf)
                /usr/sbin/kissparms -p $1 -f y -l 50 -r 128 -s 100 -t 300
        ;;
        bc0)
                /usr/sbin/kissparms -p $1 -f n -l 200 -r 128 -s 100 -t 200
        ;;
        sm0)
                /usr/sbin/kissparms -p $1 -f n -l 20 -r 128 -s 100 -t 200
        ;;
        axip0)
                echo ": Nothing to do for port $1"
        ;;
        *)
                echo ": Usage: do_kissparms <portname portname ... >"
        ;;
        esac

shift
done

#----- End of do_kissparms

TASK : To set parameters on a list of devices which are passed into the script as arguments either from the OS prompt or to a subroutine from inside a driving script.

To run this script from the OS prompt, you would enter something like this, for example:

  • do_kissparms bc0 vhf

Where the bc0 and vhf names are two devices that we wish to set parameters on. Note that if we entered no arguments, then $1 would be null and the loop around the cases would never be entered. The program would silently terminate. You could add a block of code near the top before the loop that would "detect" null arguments and perhaps print a usage/warning message to the screen.

Null Argument Detect
...

#----- If nothing passed in, then issue a warning
        if [ ! "$1" ]
        then
                echo "Usage: do_kissparms device device device ..."
                echo "No arguments passed; nothing to process!"
                exit
        fi

...

Suppose a device is entered which is not in the case list. Then it falls through to the "*" character which means any number of letters or numbers. In other words, this is the default case and usually no action is taken except issuing a warning about a non-existent device. In this code, the usage message is stated.

We also have another "strange" case where a device could be entered by accident or from another independently composed list. That case is for the axip0 device which is not set by kissparms. So we issue a special message noting the exception and no action is taken.

Suppose you wanted to move this code inside another script as a subroutine. You would need to encapsulate this script inside the subroutine giving it a name, such as do_kissparms, followed by a space then two paretheses "()", then an open curly brace "{", all the code above, and then a closing curly brace "}" as follows:

Subroutine Wrapper
do_kissparms ()
{

  ... all of the above code, i.e., the while loop and inner cases ...

}

#
#----- Main body of driving or master script
#

#----- A call to do_kissparms with its argument list
do_kissparms bc0 vhf hf

Could this script be improved upon? There is almost no code that exists that cannot be improved. So the answer is certainly yes. You may be able to think of some improvements right away, or you may see some changes that would suit your set up much better. For example, you can save some typing by creating a variable called KP where it accepts the name and full path of the kissparms binary:

  • KP="/usr/sbin/kissparms"

Then you would replace each full path with just the variable $KP.

There can be no doubt that the case logic structure can be considered a most useful coding device. I am sure that if you look around your system, you will find many opportunities to apply this versatile logic structure...


(Courtesy KBNorton Computer Services)