On-line Guides

How To Guides






Advanced Bash-Scripting Guide:
Prev Chapter 10. Loops and BranchesNext

10.4. Testing and Branching

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

case (in) / esac

The case construct is the shell scripting analog to 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

Note
  • Quoting the variables is not mandatory, since word splitting does not take place.

  • Each test line ends with a right paren ).

  • Each condition block ends with a double semicolon ;;.

  • The entire case block terminates with an esac (case spelled backwards).

Example 10-24. Using case

#!/bin/bash
# Testing ranges of characters.
echo; echo "Hit a key, then hit return."
read Keypress
case "$Keypress" in
 [[:lower:]] ) echo "Lowercase letter";;
 [[:upper:]] ) echo "Uppercase letter";;
 [0-9] ) echo "Digit";;
 * ) echo "Punctuation, whitespace, or other";;
esac # Allows ranges of characters in [square brackets],
 #+ or POSIX ranges in [[double square brackets.
# In the first version of this example,
#+ the tests for lowercase and uppercase characters were
#+ [a-z] and [A-Z].
# This no longer works in certain locales and/or Linux distros.
# POSIX is more portable.
# Thanks to Frank Wang for pointing this out.
# Exercise:
# --------
# As the script stands, it accepts a single keystroke, then terminates.
# Change the script so it accepts repeated 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 "Ex-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 multiple inputs,
#+ 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 $E_PARAM;; # 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

Here is an more straightforward example of command-line parameter handling:

#! /bin/bash
while [ $# -gt 0 ]; do # Until you run out of parameters . . .
 case "1ドル" in
 -d|--debug)
 # "-d" or "--debug" parameter?
 DEBUG=1
 ;;
 -c|--conf)
 CONFFILE="2ドル"
 shift
 if [ ! -f $CONFFILE ]; then
 echo "Error: Supplied file doesn't exist!"
 exit $E_CONFFILE # File not found error.
 fi
 ;;
 esac
 shift # Check next set of parameters.
done
# From Stefano Falsetto's "Log2Rot" script,
#+ part of his "rottlog" package.
# Used with permission.

Example 10-26. Using command substitution to generate the case variable

#!/bin/bash
# case-cmd.sh: Using command substitution to generate a "case" variable.
case $( arch ) in # "arch" returns machine architecture.
 # Equivalent to 'uname -m' ...
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.
select

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 # What happens if there is no 'break' here?
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 34-3.


Prev Home Next
Loop ControlUp Internal Commands and Builtins
Published under the terms of the GNU General Public License Design by Interspire

AltStyle によって変換されたページ (->オリジナル) /