| Advanced Bash-Scripting Guide: An in-depth exploration of the art of shell scripting | ||
|---|---|---|
| Prev | Chapter 10. Loops and Branches | Next |
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-22. Using case
1 #!/bin/bash 2 3 echo; echo "Hit a key, then hit return." 4 read Keypress 5 6 case "$Keypress" in 7 [a-z] ) echo "Lowercase letter";; 8 [A-Z] ) echo "Uppercase letter";; 9 [0-9] ) echo "Digit";; 10 * ) echo "Punctuation, whitespace, or other";; 11 esac # Allows ranges of characters in [square brackets]. 12 13 # Exercise: 14 # -------- 15 # As the script stands, # it accepts a single keystroke, then terminates. 16 # Change the script so it accepts continuous input, 17 # reports on each keystroke, and terminates only when "X" is hit. 18 # Hint: enclose everything in a "while" loop. 19 20 exit 0 |
Example 10-23. Creating menus using case
1 #!/bin/bash 2 3 # Crude address database 4 5 clear # Clear the screen. 6 7 echo " Contact List" 8 echo " ------- ----" 9 echo "Choose one of the following persons:" 10 echo 11 echo "[E]vans, Roland" 12 echo "[J]ones, Mildred" 13 echo "[S]mith, Julie" 14 echo "[Z]ane, Morris" 15 echo 16 17 read person 18 19 case "$person" in 20 # Note variable is quoted. 21 22 "E" | "e" ) 23 # Accept upper or lowercase input. 24 echo 25 echo "Roland Evans" 26 echo "4321 Floppy Dr." 27 echo "Hardscrabble, CO 80753" 28 echo "(303) 734-9874" 29 echo "(303) 734-9892 fax" 30 echo "revans@zzy.net" 31 echo "Business partner & old friend" 32 ;; 33 # Note double semicolon to terminate 34 # each option. 35 36 "J" | "j" ) 37 echo 38 echo "Mildred Jones" 39 echo "249 E. 7th St., Apt. 19" 40 echo "New York, NY 10009" 41 echo "(212) 533-2814" 42 echo "(212) 533-9972 fax" 43 echo "milliej@loisaida.com" 44 echo "Girlfriend" 45 echo "Birthday: Feb. 11" 46 ;; 47 48 # Add info for Smith & Zane later. 49 50 * ) 51 # Default option. 52 # Empty input (hitting RETURN) fits here, too. 53 echo 54 echo "Not yet in database." 55 ;; 56 57 esac 58 59 echo 60 61 # Exercise: 62 # -------- 63 # Change the script so it accepts continuous input, 64 #+ instead of terminating after displaying just one address. 65 66 exit 0 |
An exceptionally clever use of case involves testing for command-line parameters.
1 #! /bin/bash
2
3 case "$1" in
4 "") echo "Usage: ${0##*/} <filename>"; exit 65;; # No command-line parameters,
5 # or first parameter empty.
6 # Note that ${0##*/} is ${var##pattern} param substitution. Net result is $0.
7
8 -*) FILENAME=./$1;; # If filename passed as argument ($1) starts with a dash,
9 # replace it with ./$1
10 # so further commands don't interpret it as an option.
11
12 * ) FILENAME=$1;; # Otherwise, $1.
13 esac |
Example 10-24. Using command substitution to generate the case variable
1 #!/bin/bash 2 # Using command substitution to generate a "case" variable. 3 4 case $( arch ) in # "arch" returns machine architecture. 5 i386 ) echo "80386-based machine";; 6 i486 ) echo "80486-based machine";; 7 i586 ) echo "Pentium-based machine";; 8 i686 ) echo "Pentium2+-based machine";; 9 * ) echo "Other type of machine";; 10 esac 11 12 exit 0 |
A case construct can filter strings for globbing patterns.
Example 10-25. Simple string matching
1 #!/bin/bash
2 # match-string.sh: simple string matching
3
4 match_string ()
5 {
6 MATCH=0
7 NOMATCH=90
8 PARAMS=2 # Function requires 2 arguments.
9 BAD_PARAMS=91
10
11 [ $# -eq $PARAMS ] || return $BAD_PARAMS
12
13 case "$1" in
14 "$2") return $MATCH;;
15 * ) return $NOMATCH;;
16 esac
17
18 }
19
20
21 a=one
22 b=two
23 c=three
24 d=two
25
26
27 match_string $a # wrong number of parameters
28 echo $? # 91
29
30 match_string $a $b # no match
31 echo $? # 90
32
33 match_string $b $d # match
34 echo $? # 0
35
36
37 exit 0 |
Example 10-26. Checking for alphabetic input
1 #!/bin/bash
2 # Using "case" structure to filter a string.
3
4 SUCCESS=0
5 FAILURE=-1
6
7 isalpha () # Tests whether *first character* of input string is alphabetic.
8 {
9 if [ -z "$1" ] # No argument passed?
10 then
11 return $FAILURE
12 fi
13
14 case "$1" in
15 [a-zA-Z]*) return $SUCCESS;; # Begins with a letter?
16 * ) return $FAILURE;;
17 esac
18 } # Compare this with "isalpha ()" function in C.
19
20
21 isalpha2 () # Tests whether *entire string* is alphabetic.
22 {
23 [ $# -eq 1 ] || return $FAILURE
24
25 case $1 in
26 *[!a-zA-Z]*|"") return $FAILURE;;
27 *) return $SUCCESS;;
28 esac
29 }
30
31
32
33 check_var () # Front-end to isalpha().
34 {
35 if isalpha "$@"
36 then
37 echo "$* = alpha"
38 else
39 echo "$* = non-alpha" # Also "non-alpha" if no argument passed.
40 fi
41 }
42
43 a=23skidoo
44 b=H3llo
45 c=-What?
46 d=`echo $b` # Command substitution.
47
48 check_var $a
49 check_var $b
50 check_var $c
51 check_var $d
52 check_var # No argument passed, so what happens?
53
54
55 # Script improved by S.C.
56
57 exit 0 |
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-27. Creating menus using select
1 #!/bin/bash 2 3 PS3='Choose your favorite vegetable: ' # Sets the prompt string. 4 5 echo 6 7 select vegetable in "beans" "carrots" "potatoes" "onions" "rutabagas" 8 do 9 echo 10 echo "Your favorite veggie is $vegetable." 11 echo "Yuck!" 12 echo 13 break # if no 'break' here, keeps looping forever. 14 done 15 16 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-28. Creating menus using select in a function
1 #!/bin/bash
2
3 PS3='Choose your favorite vegetable: '
4
5 echo
6
7 choice_of()
8 {
9 select vegetable
10 # [in list] omitted, so 'select' uses arguments passed to function.
11 do
12 echo
13 echo "Your favorite veggie is $vegetable."
14 echo "Yuck!"
15 echo
16 break
17 done
18 }
19
20 choice_of beans rice carrots radishes tomatoes spinach
21 # $1 $2 $3 $4 $5 $6
22 # passed to choice_of() function
23
24 exit 0 |
See also Example 35-3.