In addition to the while and until loops described in the previous chapter, Qshell includes several other control structures: for , select , and case . You will learn how to use these structures in this chapter.
The for looping mechanism executes a body of commands once for each of a list of values. Its syntax is as follows :
for variable [ in value ...] do list done
Here are some things to keep in mind when using for loops:
Figures 8.1 and 8.2 provide simple examples of the for loop. In Figure 8.1, the loop runs three times. The first time the suf variable has the value txt , so Qshell lists the names of all text files. The second time suf has the value csv , so Qshell attempts to list the names of csv files, but there are none. On the third iteration, suf has the value qsh, a suffix used by convention to denote Qshell scripts.
for suf in txt csv qsh; do echo "==$suf files=="; ls *.$suf; done ==txt files== edity.txt goodoleboys.txt temp.txt test1a.txt ftpmodel.txt myfile.txt test1.txt upper.txt ==csv files== ls: 001-2113 Error found getting information for object *.csv. No such path or directory. ==qsh files== arglist.qsh echoprocess.qsh theirscript.qsh yourscript.qsh bu.qsh myscript.qsh tscript.qsh donde.qsh read002.qsh while001.qsh
Figure 8.1: This simple for loop runs three times.
# script name: bu.qsh # backup files to bu directory # # If directory bu does not exist within the current directory, create it. # If bu exists but is not a directory, error out of the script. # if [[ -e bu ]] then if [[ ! -d bu ]] then print -u2 "bu is not a directory -- backup aborted." exit 1 fi else mkdir bu fi # Backup each file named in the positional parameters. for file do cp $file bu/$file.bu done
Figure 8.2: The for loop in this example processes positional parameters.
The for loop in Figure 8.2 omits the word in and the list of values, so the loop processes the positional parameters. Each input file is copied to a file of the same name, with . bu appended, in the bu directory that is directly under the current directory.
The select construct was added to Qshell in V5R2. It is almost identical to for in its syntax, and like for , is a loop. Unlike the for construct, however, a select doesn't terminate on its own. It is usually ended with a break command. The select syntax is as follows :
select variable in value ... do list done
The select construct displays a menu of options from which the user may choose. The menu text is written to the standard error device (stderr), not the standard output device ( stdout ). The choices are numbered sequentially, beginning with one. Qshell places the selected choice number in the variable REPLY. The text of the selected choice is placed in the variable that follows the word select .
Figure 8.3 shows one way to use a select loop.
cat myscript.qsh select typ in Text CSV "Java (source)" "Java (class)" Qshell Quit do case $REPLY in 1) print "====Text files===============" ls *.txt *.csv;; 2) print "====CSV files================" ls *.csv;; 3) print "====Java source code=========" ls *.java;; 4) print "====Classes==================" ls *.class;; 5) print "====Qshell scripts===========" ls *.qsh;; 6) break;; esac done /home/jsmith $ myscript.qsh 1) Text 2) CSV 3) Java (source) 4) Java (class) 5) Qshell 6) Quit #? 1 ====Text files=============== edity.txt mydata.csv ftpmodel.txt myfile.txt goodoleboys.txt temp.txt 1) Text 2) CSV 3) Java (source) 4) Java (class) 5) Qshell 6) Quit #? 4 ====Classes================== ls: 001-2113 Error found getting information for object *.class. No such path or directory. 1) Text 2) CSV 3) Java (source) 4) Java (class) 5) Qshell 6) Quit #? 6 /home/jsmith $
Figure 8.3: The myscript.qsh script displays a menu of file types and displays certain files depending on the user's choice.
In Figure 8.4, the PS3 variable has been assigned the value "Make a selection," which is more meaningful than the default #? prompt. An asterisk has been added to the loop after option 6 as a "catch-all," to generate an error message when an invalid option is entered.
cat myscript.qsh PS3='Make a selection.' select typ in Text CSV "Java (source)" "Java (class)" Qshell Quit do case $REPLY in 1) print "====Text files===============" ls *.txt *.csv;; 2) print "====CSV files================" ls *.csv;; 3) print "====Java source code=========" ls *.java;; 4) print "====Classes==================" ls *.class;; 5) print "====Qshell scripts===========" ls *.qsh;; 6) break;; *) print -u2 "Invalid option; try again.";; esac done /home/JSMITH $ myscript.qsh 1) Text 2) CSV 3) Java (source) 4) Java (class) 5) Qshell 6) Quit Make a selection. 4 ====Classes================== Arguable.class MakeCSV.class ExecutiveDecisionMaker.class Restaurant.class J1.class RestaurantPicker.class LooselyComparableString.class 1) Text 2) CSV 3) Java (source) 4) Java (class) 5) Qshell 6) Quit Make a selection. 9 Invalid option; try again. 1) Text 2) CSV 3) Java (source) 4) Java (class) 5) Qshell 6) Quit Make a selection. 6 /home/JSMITH $
Figure 8.4: This version of myscript.qsh adds a more meaningful prompt and basic error-trapping.
The case construct is a decision-making structure, like if . However, case bases decisions on patterns of strings, rather than on exit status. The syntax of case is shown here:
case value in pattern) list ;; [ pattern) list ;; ] ... esac
A pattern is a string literal. It may include the wildcard characters listed in Table 8.1. Any character that is not a wildcard is to be matched exactly.
Matches zero or more characters
Matches a single character
Matches against groups and/or ranges of characters
To match one of the wildcard characters, precede it with a backslash in the pattern.
Each pattern is followed by a close parenthesis. There is no matching open parenthesis. Following the close parenthesis are one or more commands to be performed when the pattern matches the value. The last command for each pattern is followed by two semicolons.
The following pages provide several examples demonstrating how to use case . The first example uses a case command to convert an English word (stored in the first positional parameter) to a month number:
case "" in [Jj][Aa]*) month=1;; [Ff]*) month=2;; [Mm][Aa][Rr]*) month=3;; [Aa][Pp]*) month=4;; [Mm][Aa][Yy]*) month=5;; [Jj][Uu][Nn]*) month=6;; [Jj][Uu][Ll]*) month=7;; [Aa][Uu]*) month=8;; [Ss]*) month=9;; [Oo]*) month=10;; [Nn]*) month=11;; [Dd]*) month=12;; *) month=0;; esac
The word that will be converted by this case command may be entered in any combination of uppercase and lowercase letters . Only enough of the word to ensure uniqueness is required. Any characters beyond those required for uniqueness are ignored and do not have to be valid. For example, the values S, s, sept, September, soap, and sugar all cause Qshell to assign a value of nine to the month variable. If the word does not match the unique portion of any month name , the final pattern, a single asterisk, sets the month variable to zero.
The case command below converts a month number to a quarter number:
case "" in [1-3]) quarter=1;; [4-6]) quarter=2;; [7-9]) quarter=3;; 1[0-2]) quarter=4;; *) quarter=0;; esac
The first positional parameter in this example is a month number from one to 12. The first pattern matches any one-digit number in the range one through three. The fourth pattern matches a one followed by a number from zero to two. The last case matches anything that doesn't fit the other four cases.
The following is an improvement over the previous example, in that months one through nine may be either one or two digits long:
case "" in 0[1-3] [1-3]) quarter=1;; 0[4-6] [4-6]) quarter=2;; 0[7-9] [7-9]) quarter=3;; 1[0-2]) quarter=4;; *) quarter=0;; esac
The vertical bar is an Or operator. It indicates that either of two patterns is acceptable for a match. Therefore, the first pattern, for example, matches any of these values: 01, 02, 03, 1, 2, 3.
Here is one more example of the case structure:
allfiles=off dirfiles=off regfiles=off case "" in * ) allfiles=on;; d ) dirfiles=on;; r ) regfiles=on;; * ) print -u2 "Parameter 1 is invalid." exit 1;; esac
In this example, the asterisk in the first pattern is escaped with a backslash, so the first pattern matches an asterisk. The second and third patterns match lower-case d and lowercase r , respectively. The last pattern matches anything that did not match the first three parameters, including uppercase D and uppercase R . The asterisk in the last pattern is a wildcard, which matches any value that did not match the previous patterns. Matching the last pattern causes two actions: an error message is sent to the standard error device, and the script exits with a status of one.
In this chapter, you have been introduced to the Qshell control structures that are not controlled by the exit status of utilities and scripts. The for structure iterates through a list of values. The case structure, like if , permits decision-making.
The select structure gets user input. It is similar to for in that it is iterative. It is also similar to case in that it executes actions based on a pattern.