|
|
1.1 root 1: .TI CSHELL/FLOWCONTROL
2: Control Statements: Branching and Looping
3:
4:
5: In the C shell, control statements are special built-in commands
6: that you enter around or near conventional commands,
7: often on separate lines.
8: They let you tell the shell how to control the execution
9: of the commands they affect.
10: This means that instead of having the shell execute all your commands
11: one after another as it does normally,
12: you can have it skip some or repeat some.
13: This ability to skip or repeat commands, called branching and looping,
14: respectively, is more powerful than it might sound at first.
15:
16: Branching control statements are truly useful only inside shell scripts,
17: where the shell reads and executes commands from a file
18: instead of from the terminal.
19: Inside the script they serve mainly to let you store several
20: different command sequences compactly,
21: specifying ahead of time on what basis you want the shell to
22: skip or choose certain statements while the script is running.
23: The basis for decisions made while the script is running
24: is usually furnished by such things as arguments given to the script,
25: the time of day, whether a certain file exists, etc.
26: You enter decision points using a special notation called expressions.
27:
28: Looping control statements are useful both inside shell scripts
29: and when entered from the terminal.
30: Although they are used to execute commands repeatedly,
31: you seldom want to repeat the exact same command, but usually
32: one that is close to it.
33: To accomplish this you use a special notation called variables to indicate
34: where in a command the shell is to substitute the piece of text that
35: is to change when the command is repeated.
36:
37: In general you can best exploit control statements by using them
38: non-interactively, that is, by putting them in command scripts
39: so that they decide in your absence which commands to execute,
40: when, and how often.
41: On the other hand, you can use some of the looping statements
42: interactively to great advantage.
43:
44: How can a script of commands do different things from one run to
45: the next if the text of the commands does not change?
46: The answer is: Only if parts of the commands and decisions
47: have been entered by you with the special notation for variables
48: and expressions.
49: You need a basic working knowledge of variables and expressions
50: to understand control statements.
51: The next example illustrates the only control structure not requiring
52: any special knowledge.
53:
54: A Simple Example
55:
56: Normally in a shell script or a terminal session, when the shell
57: finishes doing one command, it proceeds to do the command
58: on the next line, and then the next, and so forth.
59: This usually continues until the end of the script file is reached or
60: the end of the terminal session (logout).
61: Suppose that instead of this normal behavior
62: you wanted to execute the same command 10 times.
63: In particular, suppose that you wanted 10 lineprinted
64: copies of the file "letter".
65: The line,
66: .IP
67: repeat 10 lpr letter
68: .LP
69: would use the control statement "repeat" to
70: cause the command "lpr letter" to be run 10 times.
71: Lines such as these are sometimes called control structures
72: because they consist of a control part and a command part.
73:
74: A More Complicated Example
75:
76: Most control structures involve several lines and require variables.
77: Here is an example of a 4-line structure which looks up some
78: words in the Unix dictionary and then displays on the terminal
79: all occurrences of them in a file called "paper".
80: .IP
81: 'nf
82: foreach x (sharp ternimal prestidigitation aint unix)
83: look $x
84: grep $x paper
85: end
86: .fi
87: .LP
88: The "foreach" line tells the shell that it must first scan the succeeding
89: lines until it finds one with the keyword "end".
90: The two intervening commands will be executed in the order they appear
91: as many times as there are words in parentheses.
92: Before each cycle of the loop begins,
93: the variable "x" is set to the next word in the list.
94: In the first cycle, the shell executes the two commands
95: using "$x" to signify the word "sharp", that is, it performs the command
96: "look sharp" followed by "grep sharp paper".
97: Then the shell proceeds with the next word on the list, this time
98: performing the two commands using "$x" to signify the
99: mispelled word "ternimal".
100: This continues for each word on the list until they have all been used,
101: at which point the shell resumes its normal operation by
102: proceeding to the next command, if any, after the "end" statement.
103:
104: A Summary of Control Statements
105:
106: There is rarely a situation that requires the use of one
107: kind of control statement over another, although some kinds
108: are more convenient than others in certain circumstances.
109: Here is a summary of the C shell's control statements indicating the
110: general form of the first line, special associated words that accompany
111: the statement on other lines, whether it is practical to
112: use interactively, and the usual purpose.
113:
114: .nf
115: .ne 8
116: Name and First Line Associated Keywords Intr? Usual Purpose
117: ---------------------------------------------------------------------------
118: if (expression) then, else, endif no conditional branching
119: repeat N command yes simple repetition
120: while (expression) end, break, continue yes ... until a condition
121: foreach x (list) end, break, continue yes ... for each item in list
122: goto label label: no unconditional branching
123: switch (expression) case, endsw, breaksw no multi-way value branch
124:
125: .fi
126: In the examples that follow I will use C shell variables and expressions
127: as if you were already somewhat familiar with them.
128: Additional explanation sometimes appears in the form of C shell
129: comments (annotations) appended to command lines after a # sign.
130: Also, I have followed a common convention in programming languages
131: in indenting those commands grouped inside control structures.
132:
133: The \fBif\fP Statement
134:
135: This has several different forms, the first one being
136: .IP
137: \fBif (\fPexpression\fB)\fP command
138: .LP
139: where the
140: \fIcommand\fP
141: is executed only if the
142: \fIexpression\fP
143: is true.
144: Here is a script fragment that informs the user of the number
145: of files in the current directory.
146: .IP
147: 'nf
148: set x = (*) # variable x gets list of files
149: echo -n "There are $#x file" # $#x is number of files in $x
150: if ($#x > 1) echo -n "s" # if more than 1, make plural
151: echo " in this directory." # end sentence; no -n so add newline
152: .fi
153: .LP
154: Unfortunately, the first form requires the
155: \fIcommand\fP
156: to be a simple command;
157: in other words, no pipelines or multiple commands are allowed,
158: even if they are aliased.
159: You can avoid this restriction by calling a multi-line
160: script in place of \fIcommand\fP, but that takes time.
161: It would be more efficient to use the second form of the if statement,
162: .IP
163: 'nf
164: \fBif (\fPexpression\fB) then
165: \fPcommands\fB
166: ...
167: else if (\fPexpression\fB) then
168: \fPcommands\fB
169: ...
170: \&...
171: else
172: \fPcommands\fB
173: ...
174: endif\fP
175: .fi
176: .LP
177: The hardest part about this form is to remember
178: to type the keyword \fBthen\fP;
179: even the most experienced users trip over it fairly often.
180: Here is an example of a multi-line \fBif\fP statement
181: which checks to see if a file called "readme" exists
182: and has read permission; if so it displays it,
183: if not it takes other steps.
184: .IP
185: 'nf
186: if (-e readme && -r readme) then # if it exists and is readable
187: echo "I have a message." # announce that fact
188: cat readme # and display the file
189: else if (-e readme) then # else if it exists only ...
190: echo "I have a message, but cannot read it."
191: exit # complain and exit script
192: else # else if it doesn't even exist
193: /usr/games/fortune # do something irrational
194: endif # this ends the multi-line if
195: .fi
196: .LP
197:
198: The \fBrepeat\fP Statement
199:
200: As you may have gathered, the form of this control statement is
201: .IP
202: \fBrepeat \fIN command\fR
203: .LP
204: Unfortunately, the \fIcommand\fP part
205: suffers the same restrictions here as in the one-line if statement.
206: As a result, it is hard to find uses for the repeat statement.
207: One example cited occasionally uses it to make 100 copies of one
208: file in another file, as in
209: .IP
210: repeat 100 cat data >> bigdata
211: .LP
212: A much more efficient way to do this is
213: .IP
214: cat `jot -b data 100` > bigdata
215: .LP
216: using command substitution
217: (those quote marks should be grave accents).
218: Similarly, our earlier example with "lpr" would have
219: been better done with
220: .IP
221: lpr `jot -b letter 10`
222: .LP
223:
224: The \fBwhile\fP Statement
225:
226: The general form of the while statement is
227: .IP
228: 'nf
229: \fBwhile (\fPexpression\fB)
230: commands
231: ...
232: end\fP
233: .fi
234: .LP
235: There are no clear cut criteria for determining when to use this
236: control statement over another, but in general, use it when
237: you want to loop through a sequence of commands until a
238: certain condition becomes false.
239: On each iteration (cycle through the loop), if \fIexpression\fP
240: is true the shell executes the commands up until the
241: "end" statement, then begins the next iteration.
242: When \fIexpression\fP becomes false, execution continues after
243: the "end".
244:
245: You can enter the while statement at your terminal as well
246: as in a script.
247: At the terminal, the shell cannot begin processing until the
248: entire body of commands up to the "end" have been entered,
249: so each time you press RETURN, it reminds you that it still
250: expects you to type in "end" sometime by prompting with a "?".
251: For example, to print out the numbers from 1 to 300 on your
252: terminal with an interactive while loop,
253: .IP
254: 'nf
255: % set n = 1 # variable n starts out 1
256: % while ($n < 301) # while it's less than 301
257: ? echo $n # print it out; shell prompt is "?"
258: ? @ n = ($n + 1) # @ is like set, but do arithmetic
259: ? end # end of loop; loop starts after RETURN
260: .fi
261: .LP
262: A better way to count to 300 would be "jot 300",
263: but this example illustrates three points.
264: First, most applications in the C shell that require doing
265: a command sequence a certain number of times have this general form.
266: The condition that the while loop is waiting to become false
267: is an arithmetic relation involving a variable that is
268: initialized before the loop, changes inside the loop,
269: and is tested in the \fIexpression\fP.
270: Secondly, any number of commands could have filled up the
271: body of the loop, so this provides a way to get around
272: the restriction in the repeat statement of repeating
273: only a simple command.
274: The counting variable, "n" in this case, need not always be
275: used except to be incremented.
276: Finally, the variable could have been used to count down
277: instead of up, to increase by larger increments,
278: or to change using any arithmetic operation.
279: The \fIexpression\fP could easily be adjusted to test
280: for different arithmetic relations.
281:
282: As another example, suppose several users need to edit a file called
283: "project" from time to time, the contents of which are important
284: enough that only one user should be allowed to edit it at a time.
285: All the users involved could accomplish this by agreeing among
286: themselves only to edit "project" using the script below.
287: The while loop causes the shell to
288: check every minute whether a file called "lock" exists.
289: When it no longer exists, the shell creates the file again,
290: letting other users know that someone is currently editing "project",
291: and calls up the editor.
292: The lock file is removed after editing, and the script is done.
293: .IP
294: 'nf
295: while (-e lock) # while file "lock" exists ...
296: echo Trying # tells you that you (still) must wait
297: sleep 60 # do nothing for 60 seconds
298: end # end of loop; when "lock" gone, proceed
299: date > lock # recreate "lock", no matter what with
300: vi project # call up vi on data being secured
301: rm lock # let others know they can edit "project"
302: .fi
303: .LP
304: The \fBforeach\fP Statement
305:
306: The general form of the foreach statement is
307: .IP
308: 'nf
309: \fBforeach \fPvar\fB (\fPword1 word2 \fB...)
310: commands
311: ...
312: end\fR
313: .fi
314: .LP
315: As for the while statement there are no clear cut
316: criteria for determining when to use the foreach
317: statement over another, but in general, use it when
318: you want to loop through a sequence of commands to be
319: applied to a list of items.
320: On each iteration, the variable \fIvar\fP is set to
321: the next word in the parenthesized list.
322: The shell executes the commands up until the
323: "end" statement, then begins the next iteration.
324: When there are no unused words left in the list,
325: execution continues after the "end".
326: The variable \fIvar\fP may be any name you choose,
327: and you are not required to use it in the body of the loop.
328: Also, the foreach statement is probably used interactively
329: more often than any other control structure.
330:
331: For example, during a terminal session you could
332: mail the files "data1", "data2", etc. up to "data7"
333: to a user named "fred" using
334: .IP
335: 'nf
336: % foreach f (data1 data2 data3 data4 data5 data6 data7)
337: ? mail fred < $f
338: ? end
339: .fi
340: .LP
341: The list could have been abbreviated with (data[1-7]).
342: All the C shell control structures can be nested (used
343: inside one another).
344: As an example, the following script copies each of the data files
345: above into each of the directories "old", "new", and "current".
346: .IP
347: 'nf
348: foreach d (old new current) # outer loop for directories
349: mkdir $d # make the target directory
350: foreach f (data[1-7]) # inner loop for files
351: cp $f $d # copy (is this efficient?)
352: end # end of inner loop
353: end # end of outer loop
354: .fi
355: .LP
356: This example above is actually rather inefficient
357: because the inner loop (3 statements) could have been
358: replaced with the single command "cp data[1-7] $d",
359: so that the shell would only create one "cp" process
360: per directory instead of 7.
361: The lesson to learn is that however tempted you may be
362: to use the foreach statement, check to see if the command
363: involved takes more than one argument.
364:
365: The \fBgoto\fP and \fBonintr\fP Statements
366:
367: The goto statement consists of the word \fBgoto\fP
368: followed be a \fIlabel\fP and causes the shell to jump to a
369: line beginning with the string \fIlabel\fP
370: followed by a : and to begin executing statements there.
371: Strictly speaking all of the C shell's branching and
372: looping constructs could be built up using combinations
373: of just goto and simple if statements.
374: Computer scientists tend to be fanatical in their opposition
375: to the goto statement, so try not use it in their presence.
376: Here is an example of a script that runs "sed" twice
377: on each of the command line arguments.
378: When there are no arguments left, the script removes
379: a temporary file it created and exits.
380: If an argument file is really a directory,
381: an error message is printed and the temporary file is not removed.
382: .IP
383: 'nf
384: foreach f ($argv) # foreach command argument
385: if (-d $f) goto error # if a directory, goto error
386: sed -f pass1 $f > /tmp/xyz$$ # sed stores in /tmp/xyz$$
387: sed -f pass2 /tmp/xyz$$ > $f # then stores back in $f
388: end # end of foreach
389: goto done: # skip the error part
390: error: # just label, no other effect
391: echo Error - $f is a directory. # print message
392: goto end: # exit script avoiding "rm"
393: done: # just a label
394: rm /tmp/xyz$$ # remove temporary file
395: end: # last label, nothing after it
396: .LP
397: Besides being clumsy, this script would not remove its temporary
398: file if the user typed an interrupt to abort the execution prematurely;
399: the script would just drop in its tracks.
400: This is remedied by the onintr statement, which has the form
401: \fBonintr \fIlabel\fR, where \fIlabel\fP is a label to jump
402: to whenever the script receives a subsequent interrupt.
403: The next example illustrates this, and also converts the goto
404: statements into more socially acceptable statements.
405: .IP
406: 'nf
407: onintr done # on interrupt, goto done
408: foreach f ($argv) # foreach command argument
409: if (-d $f) then # if a directory then ...
410: echo Error - $f is a directory.
411: exit # exit, but leave temp file
412: endif # end of if statement
413: sed -f pass1 $f > /tmp/xyz$$ # sed stores in /tmp/xyz$$
414: sed -f pass2 /tmp/xyz$$ > $f # then stores back in $f
415: end # end of foreach
416: done: # label for onintr
417: rm /tmp/xyz$$ # remove temporary file
418: .fi
419: .LP
420: The \fBswitch\fP Statement
421:
422: This is like a multi-line if statement which branches only
423: on the value of one expression.
424: It is usually most useful for visually separating the
425: different branches taken in response to different values
426: of the same thing.
427: Without much further ado, here is a meaningless script fragment that
428: processes its arguments through a while loop using a switch statement.
429: Just before the each next interaction of the loop, the shift command
430: causes the current value of "$argv[1]" to be discarded and replaced
431: with "$argv[2]".
432: .IP
433: 'nf
434: while ($#argv > 0)
435: switch ($argv[1])
436: case -m:
437: set m
438: breaksw
439: case -l*:
440: set length = $argv[1]
441: breaksw
442: case -T:
443: set sort = (/usr/lib/sort)
444: set lpr = (/usr/ucb/lpr -Pevans1)
445: breaksw
446: case -Y:
447: set sort = (/usr/lib/vsort -W)
448: set lpr = (/usr/ucb/lpr -Pversatec)
449: breaksw
450: case -F:
451: if ($#argv < 2) then
452: echo -F takes following port name.
453: exit(1)
454: endif
455: set argv = (-1 $2.f -2 $2.p -3 $2.o $argv[3-])
456: breaksw
457: case -1:
458: case -2:
459: case -3:
460: if ($#argv < 2) then
461: echo $1 takes following port name.
462: exit(1)
463: endif
464: breaksw
465: case -x:
466: set xyzzy
467: breaksw
468: case -*:
469: set flags = ($flags $argv[1])
470: breaksw
471: endsw
472: shift argv
473: end
474: .fi
475: .LP
476:
477:
478: jak
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.