|
Softpanorama |
||||||
| Contents | Bulletin | Scripting in shell and Perl | Network troubleshooting | History | Humor | |
| Arithmetic expressions | Comparison operators | String Operations in Shell | |||
| if statements | Loops in Shell | select | Pipes in Loops | ||
| Advanced navigation | BASH Debugging | Shell history | Etc |
Shell case statement is similar to those in Pascal and switch statement in C. It can be used to test a variable against set of patterns. Often case statement lets you express a series of if-then-else statements that check single variable for various conditions or ranges in a more concise way.
The syntax of case is as follows:
case expression in
pattern1 )
statements ;;
pattern2 )
statements ;;
...
esac
Any of the patterns can actually be several patterns separated by pipe character "|". If expression matches one of the patterns, its corresponding statements of a case branch are executed. If there are several patterns separated by pipe characters, the expression can match any of them in order for the associated statements to be run. The patterns are checked in order until a match is found; if none is found, nothing happens.
For example:
for filename in $*; do
case $filename in
*.c )
objname=${filename%.c}.o
ccom $filename $objname
;;
*.s )
objname=${filename%.s}.o
as $filename $objname
;;
*.o ) ;;
* )
print "error: $filename is not a source or object file."
return 1 ;;
esac
done
The final case is *, which is a catchall for whatever didn't match
the other cases. (In fact, a * case is analogous to a default
case in C and an otherwise case in some Pascal-derived languages.)
Another example:
extension="${file##*.}"
case $extension in
"gz" )
gunzip $file
;;
"bz2" )
bz2unpack $file
;;
* )
echo "Archive format for extention '$extension' is not recognized."
;;
esac
Above, bash first expands "${file##*.}". In the code, "$file" is the name of a file, and "${file##.*}" has the effect of stripping all text except that following the last period in the filename. Then, bash compares the resultant string against the values listed cases of case statement. In this case, file extension will be compared against "gz" and "bz2" If one of those matches, then appropriate command will be executed for the value of $file.
Please note that $file can contain fully qualified filename . For example:
file="/somewhere/sample.gz"
Extension will still be extracted correctly. Of course you can split fully qualified name into directory and path and operate with them separately, for example:
file=$(basename $fully_qualified_name) dir=$(dirname $fully_qualified_name)
So far selectors in case statement were simple strings. They can be pretty complex patterns. For example here is how you can implement the functions that trims spaces both from left and right of its argument:
function trim
{
target=$1
while : # this is an infinite loop
do
case $target in
' '*) target=${target#?} ;; ## if $target begins with a space remove it
*' ') target=${target%?} ;; ## if $target ends with a space remove it
*) break ;; # no more leading or trailing spaces, so exit the loop
esac
done
return target
}
You can use "Or" operation in case to tie several cases to one branch of case statement. Here is an example from Wikipedia:
case $n in
0) echo 'You typed 0.';;
1|9) echo "$n is a perfect square.";;
3|5|7) echo "$n is a prime number.";;
4) echo "$n is a perfect square.
$n is an even number";;
2|6|8) echo "$n is an even number.";;
*) echo 'Only single-digit numbers are allowed.';;
esac
As patterns can have character classes that sometimes simplify logic or options
processing and similar cases (Using
case statements):
cat disktest.sh
#!/bin/bash
# This script does a very simple test for checking disk space.
space=`df -h | awk '{print $5}' | grep % | grep -v Use | sort -n | tail -1 | cut -d "%" -f1 -`
case $space in
[1-6]*)
Message="All is quiet."
;;
[7-8]*)
Message="Start thinking about cleaning out some stuff. There's a partition that is $space % full."
;;
9[1-8])
Message="Better hurry with that new disk... One partition is $space % full."
;;
99)
Message="I'm drowning here! There's a partition at $space %!"
;;
*)
Message="I seem to be running with an nonexistent amount of disk space..."
;;
esac
Here are few additional examples from Advanced Bash-Scripting Guide: Chapter 11. Loops and Branches
#!/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
|
#!/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 Flash Dr."
echo "Hardscrabble, CO 80753"
echo "(303) 734-9874"
echo "(303) 734-9892 fax"
echo "revans@zzy.net"
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 "milliej@loisaida.com"
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.
|
#!/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 11-27. Simple string matching
#!/bin/bash
# match-string.sh: Simple string matching.
match_string ()
{ # Exact string match.
MATCH=0
E_NOMATCH=90
PARAMS=2 # Function requires 2 arguments.
E_BAD_PARAMS=91
[ $# -eq $PARAMS ] || return $E_BAD_PARAMS
case "$1" in
"$2") return $MATCH;;
* ) return $E_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
|
#!/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.
|
|
|
||||
| Bulletin | Latest | Past week | Past month |
|
Sept 18, 2007 | The UNIX and Linux Forums
Using a case/esac statement makes regular expressions simple and obvious.
Code:
case $name in [ab]{2,3} ) echo do stuff ;; * ) ;; esacdrl:
Hi.
Perhaps I am using a too-old version of bash. Version 3.00.16(1) recognizes the [ab] but not the {2,3} part, apparently because the case selectors are expanded in the same fashion as pathnames, not regular expressions:
Code:
#!/bin/bash3 # @(#) s4 Demonstrate case selectors. set -o nounset echo echo "GNU bash $BASH_VERSION" >&2 echo " MUST USE VERSION 3 FOR REGULAR EXPRESSIONS WITH =~ OPERATOR!" # See: http://www.unix.com/showthread.php?p=302136557&posted=1#post302136557 echo # if [ $name = [ab]{2,3} ]; then name="b" name="bb" echo " Original string = \"$name\"" case $name in [ab]{2,3} ) echo Success ;; * ) echo Failure ;; esac echo name="b{2,3}" echo " Original string = \"$name\"" case $name in [ab]{2,3} ) echo Success ;; * ) echo Failure ;; esac exit 0producing:
Code:
% ./s4 GNU bash 3.00.16(1)-release MUST USE VERSION 3 FOR REGULAR EXPRESSIONS WITH =~ OPERATOR! Original string = "bb" Failure Original string = "b{2,3}" Successcheers, drl
radoulov:
Yes, it's globbing, not re's. For {2,3} with globbing:
Code:
bash 3.2.25(1)$ v=b bash 3.2.25(1)$ case $v in ([ab][ab]|[ab][ab][ab]) echo OK;;(?) echo KO;;esac KO bash 3.2.25(1)$ v=bbb bash 3.2.25(1)$ case $v in ([ab][ab]|[ab][ab][ab]) echo OK;;(?) echo KO;;esac OK bash 3.2.25(1)$ # or: bash 3.2.25(1)$ shopt -s extglob bash 3.2.25(1)$ v=b bash 3.2.25(1)$ case $v in ([ab][ab]?([ab])) echo OK;;(?) echo KO;;esac KO bash 3.2.25(1)$ v=bb bash 3.2.25(1)$ case $v in ([ab][ab]?([ab])) echo OK;;(?) echo KO;;esac OK
08-20-2008 | The UNIX and Linux Forums
Hello all, I'm a new poster with a problem that I'm not finding in the archives.
I'm trying to better my understanding of the behavior of case statements, in particular the word to pattern matching aspects. According to the GNU bash reference (Bash Reference Manual) the syntax is:
case word in [ [(] pattern [| pattern]...) command-list ;;]... esac
Also, "Each pattern undergoes tilde expansion, parameter expansion, command substitution, and arithmetic expansion."
Based on this I would expect both of these expressions to behave identically:
#! /bin/bash # test program attempting to understand case statements # predominantly used/tested by author in bash --version # GNU bash, version 3.2.39(1)-release (i686-pc-linux-gnu) # Copyright (C) 2007 Free Software Foundation, Inc. # please note that above version is as installed by gentoo # also tested using completely unmodified/unpatched bash --version # GNU bash, version 3.2.0(1)-release (i686-pc-linux-gnu) # Copyright (C) 2005 Free Software Foundation, Inc. # Usage: $0 [ {1..10} ] VAR=6 if [ $# -gt 0 ] then VAR=$1 fi echo VAR=$VAR case $VAR in [1..5] ) echo "one to five" ;; $(seq -s'|' 6 10) ) echo "six to ten" ;; # 6|7|8|8|10 ) echo "six to ten" ;; * ) echo "fall through" ;; esac exit 0Interesting. I replaced seq with echo "6|7|8|9|10" just to take seq out of the equation. The script works as expected with ksh93 but fails on bash (version 3.2.29)It seems to interpret the expanded string literally.
Code:
bash$ case '6|7|8|9|10' in $(seq -s '|' 6 10) ) echo yes;; esac yesIt would surprise me if the spec is unambiguous on this point ...
stryth
Quote:
It seems to interpret the expanded string literally.
Code:
bash$ case '6|7|8|9|10' in $(seq -s '|' 6 10) ) echo yes;; esac yesIt would surprise me if the spec is unambiguous on this point ...
You nailed it. Sifting through the source was revealing:
case 3 in 2|3|4 )
The above produces a linked list of strings 2->3->4, which are in turn compared as strings against the 3 in the case. When a variable or command substitution is used the substitution happens but is never parsed for comparison of individual components.
This appears to be a bug and has been submitted as such.
ghostdog74:
Code:
shopt -s extglob case $var in @($(seq -s'|' 6 10)) ) echo "six to ten" ;; 6|7|8|8|10 ) echo "dfs six to ten" ;; esac shopt -u extglobSee here under 3.5.8.1 for explanation
drl
Hi.
My interpretation of this is different from the previous ones. The bash manual does indeed have the words cited. And it is clear that substitution takes place. However, the result is simply a string. The manual does not say that the case statement is then re-scanned to cause the strings to be treated as patterns.
We know how to cause a re-scan: the eval built-in:
Code: #!/bin/bash3 -
# @(#) s2 Demonstrate eval of case to get active selectors.
echo echo "(Versions displayed with local utility \"version\")" version >/dev/null 2>&1 && version "=o" $(_eat $0 $1) set -o nounset
echo VAR=6
if [ $# -gt 0 ] then VAR=$1 fi
echo VAR=$VAR six=$(seq -s'|' 6 10) echo " Generated selector in variable: $six"
echo echo " Results of eval case:" eval " case $VAR in $(seq -s'|' 1 5) ) echo ' 1 - 5 (seq)' ;; $six) echo 'six to ten (variable)' ;; * ) echo 'fall through' ;; esac "
exit 0 Producing:
Code: $ ./s2 5
(Versions displayed with local utility "version") Linux 2.6.11-x1 GNU bash 3.00.16(1)-release
VAR=5 Generated selector in variable: 6|7|8|9|10
Results of eval case: 1 - 5 (seq) $ $ ./s2 7
(Versions displayed with local utility "version") Linux 2.6.11-x1 GNU bash 3.00.16(1)-release
VAR=7 Generated selector in variable: 6|7|8|9|10
Results of eval case: six to ten (variable) This may not be what we want or expect, and the interpretation may not even be correct. We might consider this a method to get around a limitation. In any case (pun intended), it works ... cheers, drl
- The basic form of the case statement is:
case expression in pattern1 ) statements ;; pattern2 ) statements ;; . . . esac
- The statements corresponding to the first pattern matching the expression are executed, after which the case statement terminates.
- The expression is usually some variable's value.
- The patterns can be plain strings, or they can be Korn shell patterns using *, ?, !, [], etc. (like file-matching patterns).
- A pattern can consist of several patterns separated by | (logical or), and the pattern can also be written as
(pattern).
|
|
||||
| Bulletin | Latest | Past week | Past month |
|
Most popular humor pages:
Manifest of the Softpanorama IT Slacker Society : Ten Commandments of the IT Slackers Society : Computer Humor Collection : BSD Logo Story : The Cuckoo's Egg : C++ Humor : ARE YOU A BBS ADDICT? : Object oriented programmers of all nations : C Humor : Financial Humor : Financial Humor Bulletin, 2008 : Financial Humor Bulletin, 2010 : Richard Stallman Related Humor : Admin Humor : Perl-related Humor : Linus Torvalds Related humor : PseudoScience Related Humor : Networking Humor : Shell Humor: Financial Humor Bulletin, 2011 : Financial Humor Bulletin, 2012 : Financial Humor Bulletin, 2013 : Java Humor : Software Engineering Humor : Sun Solaris Related Humor : The Most Comprehensive Collection of Editor-related Humor : Microsoft plans to buy Catholic Church : Education Humor : IBM Humor : Assembler-related Humor : VIM Humor Computer Viruses Humor : Bright tomorrow is rescheduled to a day after tomorrow : Classic Computer Humor : Best Russian Programmer Humor : Russian Musical Humor : The Perl Purity Test : Politically Incorrect Humor : GPL-related Humor : OFM Humor : IDS Humor : Real Programmers Humor : Scripting Humor : Web Humor : Programming Language Humor : Goldman Sachs related humor : Greenspan humor :
Copyright © 1996-2013 by Dr. Nikolai Bezroukov. www.softpanorama.org was created as a service to the UN Sustainable Development Networking Programme (SDNP) in the author free time. This document is an industrial compilation designed and created exclusively for educational use and is distributed under the Softpanorama Content License. Site uses AdSense so you need to be aware of Google privacy policy. Original materials copyright belong to respective owners. Quotes are made for educational purposes only in compliance with the fair use doctrine. This is a Spartan WHYFF (We Help You For Free) site written by people for whom English is not a native language. Grammar and spelling errors should be expected. The site contain some broken links as it develops like a living tree...
|
|
You can use PayPal to make a contribution, supporting hosting of this site with different providers to distribute and speed up access. Currently there are two functional mirrors: softpanorama.info (the fastest) and softpanorama.net. |
Disclaimer:
The statements, views and opinions presented on this web page are those of the author and are not endorsed by, nor do they necessarily reflect, the opinions of the author present and former employers, SDNP or any other organization the author may be associated with. We do not warrant the correctness of the information provided or its fitness for any purpose.
Last modified: January 11, 2013