The case and select constructs are technically not loops, since they do not iterate the execution of a code block. Like loops, however, they direct program flow according to conditions at the top or bottom of the block.
Controlling program flow in a code block
The case construct is the shell equivalent of switch in C/C++. It permits branching to one of a number of code blocks, depending on condition tests. It serves as a kind of shorthand for multiple if/then/else statements and is an appropriate tool for creating menus.
case "$variable" in
�"$condition1" )
�command...
�;;
�"$condition2" )
�command...
�;;
esac
![]() |
|
Example 10-24. Using case
#!/bin/bash echo; echo "Hit a key, then hit return." read Keypress case "$Keypress" in [a-z] ) echo "Lowercase letter";; [A-Z] ) echo "Uppercase letter";; [0-9] ) echo "Digit";; * ) echo "Punctuation, whitespace, or other";; esac # Allows ranges of characters in [square brackets]. # Exercise: # -------- # As the script stands, # it accepts a single keystroke, then terminates. # Change the script so it accepts continuous input, # reports on each keystroke, and terminates only when "X" is hit. # Hint: enclose everything in a "while" loop. exit 0 |
Example 10-25. Creating menus using case
#!/bin/bash # Crude address database clear # Clear the screen. echo " Contact List" echo " ------- ----" echo "Choose one of the following persons:" echo echo "[E]vans, Roland" echo "[J]ones, Mildred" echo "[S]mith, Julie" echo "[Z]ane, Morris" echo read person case "$person" in # Note variable is quoted. "E" | "e" ) # Accept upper or lowercase input. echo echo "Roland Evans" echo "4321 Floppy Dr." echo "Hardscrabble, CO 80753" echo "(303) 734-9874" echo "(303) 734-9892 fax" echo "[email protected]" echo "Business partner & old friend" ;; # Note double semicolon to terminate each option. "J" | "j" ) echo echo "Mildred Jones" echo "249 E. 7th St., Apt. 19" echo "New York, NY 10009" echo "(212) 533-2814" echo "(212) 533-9972 fax" echo "[email protected]" echo "Girlfriend" echo "Birthday: Feb. 11" ;; # Add info for Smith & Zane later. * ) # Default option. # Empty input (hitting RETURN) fits here, too. echo echo "Not yet in database." ;; esac echo # Exercise: # -------- # Change the script so it accepts continuous input, #+ instead of terminating after displaying just one address. exit 0 |
An exceptionally clever use of case involves testing for command-line parameters.
#! /bin/bash
case "$1" in
"") echo "Usage: ${0##*/} <filename>"; exit 65;; # No command-line parameters,
# or first parameter empty.
# Note that ${0##*/} is ${var##pattern} param substitution. Net result is $0.
-*) FILENAME=./$1;; # If filename passed as argument ($1) starts with a dash,
# replace it with ./$1
# so further commands don't interpret it as an option.
* ) FILENAME=$1;; # Otherwise, $1.
esac |
Example 10-26. Using command substitution to generate the case variable
#!/bin/bash # Using command substitution to generate a "case" variable. case $( arch ) in # "arch" returns machine architecture. i386 ) echo "80386-based machine";; i486 ) echo "80486-based machine";; i586 ) echo "Pentium-based machine";; i686 ) echo "Pentium2+-based machine";; * ) echo "Other type of machine";; esac exit 0 |
A case construct can filter strings for globbing patterns.
Example 10-27. Simple string matching
#!/bin/bash
# match-string.sh: simple string matching
match_string ()
{
MATCH=0
NOMATCH=90
PARAMS=2 # Function requires 2 arguments.
BAD_PARAMS=91
[ $# -eq $PARAMS ] || return $BAD_PARAMS
case "$1" in
"$2") return $MATCH;;
* ) return $NOMATCH;;
esac
}
a=one
b=two
c=three
d=two
match_string $a # wrong number of parameters
echo $? # 91
match_string $a $b # no match
echo $? # 90
match_string $b $d # match
echo $? # 0
exit 0 |
Example 10-28. Checking for alphabetic input
#!/bin/bash
# isalpha.sh: Using a "case" structure to filter a string.
SUCCESS=0
FAILURE=-1
isalpha () # Tests whether *first character* of input string is alphabetic.
{
if [ -z "$1" ] # No argument passed?
then
return $FAILURE
fi
case "$1" in
[a-zA-Z]*) return $SUCCESS;; # Begins with a letter?
* ) return $FAILURE;;
esac
} # Compare this with "isalpha ()" function in C.
isalpha2 () # Tests whether *entire string* is alphabetic.
{
[ $# -eq 1 ] || return $FAILURE
case $1 in
*[!a-zA-Z]*|"") return $FAILURE;;
*) return $SUCCESS;;
esac
}
isdigit () # Tests whether *entire string* is numerical.
{ # In other words, tests for integer variable.
[ $# -eq 1 ] || return $FAILURE
case $1 in
*[!0-9]*|"") return $FAILURE;;
*) return $SUCCESS;;
esac
}
check_var () # Front-end to isalpha ().
{
if isalpha "$@"
then
echo "\"$*\" begins with an alpha character."
if isalpha2 "$@"
then # No point in testing if first char is non-alpha.
echo "\"$*\" contains only alpha characters."
else
echo "\"$*\" contains at least one non-alpha character."
fi
else
echo "\"$*\" begins with a non-alpha character."
# Also "non-alpha" if no argument passed.
fi
echo
}
digit_check () # Front-end to isdigit ().
{
if isdigit "$@"
then
echo "\"$*\" contains only digits [0 - 9]."
else
echo "\"$*\" has at least one non-digit character."
fi
echo
}
a=23skidoo
b=H3llo
c=-What?
d=What?
e=`echo $b` # Command substitution.
f=AbcDef
g=27234
h=27a34
i=27.34
check_var $a
check_var $b
check_var $c
check_var $d
check_var $e
check_var $f
check_var # No argument passed, so what happens?
#
digit_check $g
digit_check $h
digit_check $i
exit 0 # Script improved by S.C.
# Exercise:
# --------
# Write an 'isfloat ()' function that tests for floating point numbers.
# Hint: The function duplicates 'isdigit ()',
#+ but adds a test for a mandatory decimal point. |
The select construct, adopted from the Korn Shell, is yet another tool for building menus.
select variable [in list]
do
�command...
�break
done
This prompts the user to enter one of the choices presented in the variable list. Note that select uses the PS3 prompt (#? ) by default, but that this may be changed.
Example 10-29. Creating menus using select
#!/bin/bash PS3='Choose your favorite vegetable: ' # Sets the prompt string. echo select vegetable in "beans" "carrots" "potatoes" "onions" "rutabagas" do echo echo "Your favorite veggie is $vegetable." echo "Yuck!" echo break # if no 'break' here, keeps looping forever. done exit 0 |
If in list is omitted, then select uses the list of command line arguments ($@) passed to the script or to the function in which the select construct is embedded.
Compare this to the behavior of a
for variable [in list]
construct with the in list omitted.Example 10-30. Creating menus using select in a function
#!/bin/bash
PS3='Choose your favorite vegetable: '
echo
choice_of()
{
select vegetable
# [in list] omitted, so 'select' uses arguments passed to function.
do
echo
echo "Your favorite veggie is $vegetable."
echo "Yuck!"
echo
break
done
}
choice_of beans rice carrots radishes tomatoes spinach
# $1 $2 $3 $4 $5 $6
# passed to choice_of() function
exit 0 |
See also Example 35-3.