From: Subject: Linux Magazine | October 2001 | NEWBIES | The Case of the Matching Patterns Date: Wed, 28 Apr 2004 13:43:01 +0200 MIME-Version: 1.0 Content-Type: multipart/related; boundary="----=_NextPart_000_0000_01C42D26.B92AE5E0"; type="text/html" X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2800.1409 This is a multi-part message in MIME format. ------=_NextPart_000_0000_01C42D26.B92AE5E0 Content-Type: text/html; charset="windows-1250" Content-Transfer-Encoding: quoted-printable Content-Location: http://www.linux-mag.com/cgi-bin/printer.pl?issue=2001-10&article=newbies Linux Magazine | October 2001 | NEWBIES | The Case of = the Matching Patterns

Linux Magazine = October=20 2001

Copyright = Linux Magazine=20 =A92001

NEWBIES=20
The Case of the = Matching=20 Patterns
by Bill=20 McCarty

This month's column continues our = exploration of the=20 bash shell's scripting facilities by investigating the=20 case statement. The case statement is = particularly=20 useful for handling the arguments of a script. It is both = powerful and=20 sophisticated, making it easy to express complex conditions that = would=20 tax your patience if you coded them using an if = statement.

The case Statement

Both if and case are conditional = statements; that=20 is, you can use them to take alternative actions based on tests = you=20 specify. The power of the case statement lies in the = kinds of=20 tests it lets you specify. While the if statement = relies on a=20 true/false value, the case statement lets you specify a = text=20 string and a series of regular expressions, which it attempts to = match=20 against the text string. When a match is found, a series of = commands is=20 executed. Here's the general form of the case = statement:

case string in

pattern) commands;;

pattern) commands;;

...

esac

You can see that the statement begins and ends with the = keywords=20 case and esac (the word case spelled=20 backwards). Within the statement, string = specifies the=20 value to be matched, followed by one or more patterns to be = matched=20 against the string. An indefinite number = of=20 pattern lines may be included in a case statement. Note = that=20 each pattern line contains a series of commands. Each command = within a=20 series is separated from the next by a semi-colon (;), and the = entire=20 series is followed by a pair of semicolons.

Here's a simple example of a case statement:

case $1 in

1) echo "The argument value is 1." ;;
  =20
2) echo "The argument value is 2." ;;
  =20
*) echo "The argument value is something other than 1 or 2." ;;
	  =20
esac

If you use a text editor to create a file named case1=20 containing this script, you can study the operation of the = case=20 statement by issuing a command such as:

sh case1 2

If you issue this command, the script will likely respond = with the=20 message:

The argument value is 2.

The August Newbies column explained how shell = arguments work.=20 If you didn't read that column, you'll soon be able to access it = on the=20 Linux Magazine Web site. The Reader's Digest = version of=20 that column is that shell arguments are sent to scripts as the = variables=20 $1, $2, ... , $9. So, the value typed = on the=20 command line after the word case1 is bound to the shell = variable $1. Try some argument values other than 2 and = see what=20 results you are able to obtain.


How the case Statement Works

As explained, the case command works by matching a = string=20 against a series of patterns. In the simple example given above, = the=20 shell variable $1 provides the string value. The = patterns=20 appear one to a line, each followed by a right parenthesis.

The first two patterns are literal patterns that match the = given=20 character. The first pattern matches the string 1, and = the=20 second matches the string 2. The third pattern consists = of the=20 metacharacter *, which matches any string.

When the shell executes a case statement, it tries = the=20 patterns in the order given. In the example, the shell tries to = match=20 the value of $1 against 1. If the match = succeeds, the=20 shell prints the message "The argument value is 1" and exits the = case statement. Otherwise, the shell tries to match the = value=20 of $1 against 2. If the match succeeds, the = shell=20 prints the message "The argument value is 2" and exits the = case=20 statement. If neither of the two preceding patterns matches the = value of=20 $1, the shell will try to match the value against = *.=20 Because the metacharacter * matches any string, this = match will=20 always succeed. So, if neither of the first two patterns = matches, the=20 shell will print the message "The argument value is something = other than=20 1 or 2."

Expanding Your Pattern Vocabulary

The power of the case statement has its root in the = language=20 used to express its patterns. The Linux shell and applications = support=20 several pattern languages, all of which are more or less the = same.=20 Table One summarizes the pattern language supported by = the=20 case statement.

In the slightly different pattern language used to specify = file=20 names, the characters . and / have special = status and=20 must be explicitly matched by a literal pattern character. = However, the=20 pattern language used by the case statement does not = play=20 favorites; for example, its metacharacter * will = readily match=20 either . or /.

You can combine literal characters with the elements given in = Table One to form even more sophisticated patterns. For = example,=20 you can use the pattern disc[01] to match the strings=20 disc0 and disc1.


Table One: = Metacharacters Used=20 in Forming case Statement Patterns

Pattern Meaning
\c Matches the literal character c
? Matches any single character
"..." or Matches the literal string enclosed within the=20 quotes
'...'
* Matches any series of characters, including the = empty=20 series
[abc] Matches any of the characters = a,b,c
a|b Matches the pattern a or b
[a-m Matches any character in the range
0-5] a-m or=20 = 0-9

Matching Multiple Words

The case statement is designed to match a single = word, so=20 matching a phrase such as Linux Magazine requires you = to=20 enclose the pattern within quotes. Here's a script that uses a=20 case statement to check if the script's argument is = Linux=20 Magazine:

case $1 in

"LinuxMagazine")echo"Match";;

*) echo "No match" ;;

esac

If you place this script in a file that is named case2 = and=20 invoke it as in the following command, you'll see the message=20 Match:

sh case2 "Linux Magazine"

However, if you invoke the script like this, you'll see the = message=20 No Match:

sh case2 Linux Magazine

Without the double quotes, the string Linux Magazine = is=20 treated as two arguments; the word Linux is bound to = the shell=20 variable $1, and the word Magazine is bound to = the=20 shell variable $2.

It's sometimes unreasonable to expect that the user of a = script will=20 quote the argument string. In such cases, you can work around = the=20 problem by specifying the case statement's string a = little=20 differently. Consider the following script:

case "$1 $2" in

"LinuxMagazine")echo"Match";;

*) echo "No match" ;;

esac

By specifying the string as "$1 $2", you perform the = quoting=20 on behalf of the script user. If you place this script in a file = that is=20 named case3 and invoke it as the following command, you = will see=20 the message Match notwithstanding the absence of quotes = in the=20 invocation:

sh case3 Linux Magazine

A More Sophisticated Example

The case statement is used extensively in the Linux = system=20 administration scripts. One reason for this is the ability of = the=20 case statement to cope with the variety of ways that a = human=20 may provide an argument or respond to a prompt. For example, = when a user=20 is asked to respond in the affirmative, a human may = respond=20 yes,Yes, or even simply y.

Suppose you want to create a script that prompts the user for = a yes=20 or no response. Such prompts are almost always used by systems = to=20 confirm the user's desire to execute an irreversible operation; = for=20 example, the rm command provides such a prompt when its = -i switch is specified. The following shows you how you = can use=20 the case command to provide a yes/no prompt:

echo -n "Do you want to continue (y/n): "

read reply

case $reply in

y*|Y*) echo Yes ;;

*)    echo No  ;;

esac

The script uses an echo command to display a = message; the=20 command's -n switch instructs the command to omit the = newline=20 character otherwise displayed as the final character of the = output. By=20 omitting the newline character, the user's response can be = provided on=20 the same line as the message.

The read command reads a line of text typed by the = user and=20 places the line in the specified environment variable, = $reply.=20 The case command tests the reply entered by the user. = If the=20 reply begins with the letter y or Y, the string = "Yes" is=20 echoed; otherwise, the string "No" is executed. For example, = here's a=20 typical interaction with the script, stored in the file=20 case4:

[bmccarty@athlon =
linuxmag]$ sh case4

Do you want to continue (y/n): you betcha

Yes

To apply this technique in your own scripts, simply replace = the=20 prompt string that is displayed by the echo command and = the=20 commands associated with the pattern lines.

Notice that if the user responds with something other than = yes or no,=20 the script is likely to interpret the response as no. You = may not=20 like this behavior, preferring that the script recognize invalid = responses and reissuing the prompt rather than assuming a = no=20 response. You can use a shell loop to make just such an = improvement=20 (which we'll look at next time).

On the Case...

That closes the case on our tour of the mighty case=20 statement. Next month's column will address the loop statements = provided=20 by the shell. The ability to conditionally repeat shell = statements,=20 which they provide, is an enormously powerful and useful = facility.

Until then, may all your cases be happy ones!


Bill McCarty is an associate professor at Azusa Pacific=20 University. He can be reached at bmccarty@apu.edu.=20

Linux Magazine = October=20 2001

Copyright = Linux Magazine=20 =A92001

------=_NextPart_000_0000_01C42D26.B92AE5E0 Content-Type: application/octet-stream Content-Transfer-Encoding: quoted-printable Content-Location: http://pagead2.googlesyndication.com/pagead/show_ads.js google_ad_url =3D '';=0A= google_random =3D (new Date()).getTime();=0A= google_org_error_handler =3D window.onerror;=0A= =0A= function quoted(str) {=0A= return (str !=3D null) ? '"' + str + '"' : '""';=0A= }=0A= =0A= function google_encodeURIComponent(str) {=0A= if (typeof(encodeURIComponent) =3D=3D 'function') {=0A= return encodeURIComponent(str);=0A= } else {=0A= return escape(str);=0A= }=0A= }=0A= =0A= function google_write_tracker(tracker_event) {=0A= var img_url =3D window.google_ad_url.replace(/pagead\/ads/, = 'pagead/imp.gif');=0A= var img_src =3D img_url + '&event=3D' + tracker_event;=0A= var img_tag =3D '';=0A= document.write(img_tag);=0A= }=0A= =0A= function google_append_url(param, value) {=0A= if (value) {=0A= window.google_ad_url +=3D '&' + param + '=3D' + value;=0A= }=0A= }=0A= =0A= function google_append_url_esc(param, value) {=0A= if (value) {=0A= google_append_url(param, google_encodeURIComponent(value));=0A= }=0A= }=0A= =0A= function google_append_color(param, value) {=0A= if (value && typeof(value) =3D=3D 'object') {=0A= value =3D value[window.google_random % value.length];=0A= }=0A= google_append_url('color_' + param, value);=0A= }=0A= =0A= function google_show_ad() {=0A= var w =3D window;=0A= w.onerror =3D w.google_org_error_handler;=0A= w.google_ad_url =3D 'http://pagead2.googlesyndication.com/pagead/ads?' = +=0A= 'client=3Dca-' + = escape(w.google_ad_client.toLowerCase()) +=0A= '&random=3D' + w.google_random;=0A= =0A= google_append_url('hl', w.google_language);=0A= google_append_url('gl', w.google_gl);=0A= google_append_url_esc('hints', w.google_hints);=0A= google_append_url('adsafe', w.google_safe);=0A= google_append_url('oe', w.google_encoding);=0A= google_append_url('lmt', w.google_last_modified_time);=0A= google_append_url_esc('alternate_ad_url', w.google_alternate_ad_url);=0A= google_append_url('alt_color', w.google_alternate_color);=0A= =0A= if (w.google_ad_format) {=0A= google_append_url_esc('format', w.google_ad_format.toLowerCase());=0A= }=0A= =0A= google_append_url('num_ads', w.google_max_num_ads);=0A= google_append_url('output', w.google_ad_output);=0A= google_append_url('adtest', w.google_adtest);=0A= if (w.google_ad_channel) {=0A= google_append_url_esc('channel', w.google_ad_channel.toLowerCase());=0A= }=0A= google_append_url_esc('url', w.google_page_url);=0A= google_append_color('bg', w.google_color_bg);=0A= google_append_color('text', w.google_color_text);=0A= google_append_color('link', w.google_color_link);=0A= google_append_color('url', w.google_color_url);=0A= google_append_color('border', w.google_color_border);=0A= google_append_url('kw_type', w.google_kw_type);=0A= google_append_url_esc('kw', w.google_kw);=0A= google_append_url_esc('contents', w.google_contents);=0A= google_append_url('num_radlinks', w.google_num_radlinks);=0A= google_append_url('max_radlink_len', w.google_max_radlink_len);=0A= google_append_url('rl_filtering', w.google_rl_filtering);=0A= =0A= w.google_ad_url =3D w.google_ad_url.substring(0, 1000);=0A= w.google_ad_url =3D w.google_ad_url.replace(/%\w?$/, '');=0A= =0A= if (google_ad_output =3D=3D 'js' && w.google_ad_request_done) {=0A= document.write('');=0A= } else if (google_ad_output =3D=3D 'html') {=0A= if (w.name =3D=3D 'google_ads_frame') {=0A= google_write_tracker('reboundredirect');=0A= } else {=0A= document.write('');=0A= google_write_tracker('noiframe');=0A= document.write('');=0A= }=0A= }=0A= =0A= w.google_ad_frameborder =3D null;=0A= w.google_ad_format =3D null;=0A= w.google_page_url =3D null;=0A= w.google_language =3D null;=0A= w.google_gl =3D null;=0A= w.google_hints =3D null;=0A= w.google_safe =3D null;=0A= w.google_encoding =3D null;=0A= w.google_ad_output =3D null;=0A= w.google_max_num_ads =3D null;=0A= w.google_ad_channel =3D null;=0A= w.google_contents =3D null;=0A= w.google_alternate_ad_url =3D null;=0A= w.google_alternate_color =3D null;=0A= w.google_color_bg =3D null;=0A= w.google_color_text =3D null;=0A= w.google_color_link =3D null;=0A= w.google_color_url =3D null;=0A= w.google_color_border =3D null;=0A= w.google_adtest =3D null;=0A= w.google_kw_type =3D null;=0A= w.google_kw =3D null;=0A= w.google_num_radlinks =3D null;=0A= w.google_max_radlink_len =3D null;=0A= w.google_rl_filtering =3D null;=0A= }=0A= =0A= function google_error_handler(message, url, line) {=0A= google_show_ad();=0A= return true;=0A= }=0A= =0A= window.onerror =3D google_error_handler;=0A= =0A= if (window.google_ad_frameborder =3D=3D null) {=0A= google_ad_frameborder =3D 0;=0A= }=0A= =0A= if (window.google_ad_output =3D=3D null) {=0A= google_ad_output =3D 'html';=0A= }=0A= =0A= if (window.google_ad_format =3D=3D null && window.google_ad_output = =3D=3D 'html') {=0A= google_ad_format =3D google_ad_width + 'x' + google_ad_height;=0A= }=0A= =0A= if (window.google_page_url =3D=3D null) {=0A= google_page_url =3D "Not Your Business!";=0A= if (window.top.location =3D=3D document.location) {=0A= google_page_url =3D document.location;=0A= google_last_modified_time =3D Date.parse(document.lastModified) / = 1000;=0A= }=0A= }=0A= google_show_ad();=0A= =0A= ------=_NextPart_000_0000_01C42D26.B92AE5E0--