|
|
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.