1*426d2b71SDavid du Colombier.HTML "Rc — The Plan 9 Shell 2219b2ee8SDavid du Colombier. \" /*% refer -k -e -n -l3,2 -s < % | tbl | troff -ms | lp -dfn 3219b2ee8SDavid du Colombier.Tm shell programming language g 4219b2ee8SDavid du Colombier.de TP \" An indented paragraph describing some command, tagged with the command name 5219b2ee8SDavid du Colombier.IP "\\f(CW\\$1\\fR" 5 6219b2ee8SDavid du Colombier.if \\w'\\f(CW\\$1\\fR'-4n .br 7219b2ee8SDavid du Colombier.. 8219b2ee8SDavid du Colombier.de CI 9219b2ee8SDavid du Colombier.nr Sf \\n(.f 10219b2ee8SDavid du Colombier\%\&\\$3\f(CW\\$1\fI\&\\$2\f\\n(Sf 11219b2ee8SDavid du Colombier.. 12219b2ee8SDavid du Colombier.TL 13219b2ee8SDavid du ColombierRc \(em The Plan 9 Shell 14219b2ee8SDavid du Colombier.AU 15219b2ee8SDavid du ColombierTom Duff 167dd7cddfSDavid du Colombiertd@plan9.bell-labs.com 17219b2ee8SDavid du Colombier.AB 18219b2ee8SDavid du Colombier.I Rc 19219b2ee8SDavid du Colombieris a command interpreter for Plan 9 that 20219b2ee8SDavid du Colombierprovides similar facilities to UNIX's 21219b2ee8SDavid du ColombierBourne shell, 22219b2ee8SDavid du Colombierwith some small additions and less idiosyncratic syntax. 23219b2ee8SDavid du ColombierThis paper uses numerous examples to describe 24219b2ee8SDavid du Colombier.I rc 's 25219b2ee8SDavid du Colombierfeatures, and contrasts 26219b2ee8SDavid du Colombier.I rc 27219b2ee8SDavid du Colombierwith the Bourne shell, a model that many readers will be familiar with. 28219b2ee8SDavid du Colombier.AE 29219b2ee8SDavid du Colombier.NH 30219b2ee8SDavid du ColombierIntroduction 31219b2ee8SDavid du Colombier.PP 32219b2ee8SDavid du Colombier.I Rc 33219b2ee8SDavid du Colombieris similar in spirit but different in detail from UNIX's 34219b2ee8SDavid du ColombierBourne shell. This paper describes 35219b2ee8SDavid du Colombier.I rc 's 36219b2ee8SDavid du Colombierprincipal features with many small examples and a few larger ones. 37219b2ee8SDavid du ColombierIt assumes familiarity with the Bourne shell. 38219b2ee8SDavid du Colombier.NH 39219b2ee8SDavid du ColombierSimple commands 40219b2ee8SDavid du Colombier.PP 41219b2ee8SDavid du ColombierFor the simplest uses 42219b2ee8SDavid du Colombier.I rc 43219b2ee8SDavid du Colombierhas syntax familiar to Bourne-shell users. 44219b2ee8SDavid du ColombierAll of the following behave as expected: 45219b2ee8SDavid du Colombier.P1 46219b2ee8SDavid du Colombierdate 47219b2ee8SDavid du Colombiercat /lib/news/build 48219b2ee8SDavid du Colombierwho >user.names 49219b2ee8SDavid du Colombierwho >>user.names 50219b2ee8SDavid du Colombierwc <file 51219b2ee8SDavid du Colombierecho [a-f]*.c 52219b2ee8SDavid du Colombierwho | wc 53219b2ee8SDavid du Colombierwho; date 54219b2ee8SDavid du Colombiervc *.c & 55219b2ee8SDavid du Colombiermk && v.out /*/bin/fb/* 56219b2ee8SDavid du Colombierrm -r junk || echo rm failed! 57219b2ee8SDavid du Colombier.P2 58219b2ee8SDavid du Colombier.NH 59219b2ee8SDavid du ColombierQuotation 60219b2ee8SDavid du Colombier.PP 61219b2ee8SDavid du ColombierAn argument that contains a space or one of 62219b2ee8SDavid du Colombier.I rc 's 63219b2ee8SDavid du Colombierother syntax characters must be enclosed in apostrophes 64219b2ee8SDavid du Colombier.CW ' ): ( 65219b2ee8SDavid du Colombier.P1 66219b2ee8SDavid du Colombierrm 'odd file name' 67219b2ee8SDavid du Colombier.P2 68219b2ee8SDavid du ColombierAn apostrophe in a quoted argument must be doubled: 69219b2ee8SDavid du Colombier.P1 70219b2ee8SDavid du Colombierecho 'How''s your father?' 71219b2ee8SDavid du Colombier.P2 72219b2ee8SDavid du Colombier.NH 73219b2ee8SDavid du ColombierPatterns 74219b2ee8SDavid du Colombier.PP 75219b2ee8SDavid du ColombierAn unquoted argument that contains any of the characters 76219b2ee8SDavid du Colombier.CW * 77219b2ee8SDavid du Colombier.CW ? 78219b2ee8SDavid du Colombier.CW [ 79219b2ee8SDavid du Colombieris a pattern to be matched against file names. 80219b2ee8SDavid du ColombierA 81219b2ee8SDavid du Colombier.CW * 82219b2ee8SDavid du Colombiercharacter matches any sequence of characters, 83219b2ee8SDavid du Colombier.CW ? 84219b2ee8SDavid du Colombiermatches any single character, and 85219b2ee8SDavid du Colombier.CW [\fIclass\fP] 86219b2ee8SDavid du Colombiermatches any character in the 87219b2ee8SDavid du Colombier.CW class , 88219b2ee8SDavid du Colombierunless the first character of 89219b2ee8SDavid du Colombier.I class 90219b2ee8SDavid du Colombieris 91219b2ee8SDavid du Colombier.CW ~ , 92219b2ee8SDavid du Colombierin which case the class is complemented. 93219b2ee8SDavid du ColombierThe 94219b2ee8SDavid du Colombier.I class 95219b2ee8SDavid du Colombiermay also contain pairs of characters separated by 96219b2ee8SDavid du Colombier.CW - , 97219b2ee8SDavid du Colombierstanding for all characters lexically between the two. 98219b2ee8SDavid du ColombierThe character 99219b2ee8SDavid du Colombier.CW / 100219b2ee8SDavid du Colombiermust appear explicitly in a pattern, as must the path name components 101219b2ee8SDavid du Colombier.CW . 102219b2ee8SDavid du Colombierand 103219b2ee8SDavid du Colombier.CW .. . 104219b2ee8SDavid du ColombierA pattern is replaced by a list of arguments, one for each path name matched, 105219b2ee8SDavid du Colombierexcept that a pattern matching no names is not replaced by the empty list; 106219b2ee8SDavid du Colombierrather it stands for itself. 107219b2ee8SDavid du Colombier.NH 108219b2ee8SDavid du ColombierVariables 109219b2ee8SDavid du Colombier.PP 110219b2ee8SDavid du ColombierUNIX's Bourne shell offers string-valued variables. 111219b2ee8SDavid du Colombier.I Rc 112219b2ee8SDavid du Colombierprovides variables whose values are lists of arguments \(em 113219b2ee8SDavid du Colombierthat is, arrays of strings. This is the principal difference 114219b2ee8SDavid du Colombierbetween 115219b2ee8SDavid du Colombier.I rc 116219b2ee8SDavid du Colombierand traditional UNIX command interpreters. 117219b2ee8SDavid du ColombierVariables may be given values by typing, for example: 118219b2ee8SDavid du Colombier.P1 119219b2ee8SDavid du Colombierpath=(. /bin) 120219b2ee8SDavid du Colombieruser=td 121219b2ee8SDavid du Colombierfont=/lib/font/bit/pelm/ascii.9.font 122219b2ee8SDavid du Colombier.P2 123219b2ee8SDavid du ColombierThe parentheses indicate that the value assigned to 124219b2ee8SDavid du Colombier.CW path 125219b2ee8SDavid du Colombieris a list of two strings. The variables 126219b2ee8SDavid du Colombier.CW user 127219b2ee8SDavid du Colombierand 128219b2ee8SDavid du Colombier.CW font 129219b2ee8SDavid du Colombierare assigned lists containing a single string. 130219b2ee8SDavid du Colombier.PP 131219b2ee8SDavid du ColombierThe value of a variable can be substituted into a command by 132219b2ee8SDavid du Colombierpreceding its name with a 133219b2ee8SDavid du Colombier.CW $ , 134219b2ee8SDavid du Colombierlike this: 135219b2ee8SDavid du Colombier.P1 136219b2ee8SDavid du Colombierecho $path 137219b2ee8SDavid du Colombier.P2 138219b2ee8SDavid du ColombierIf 139219b2ee8SDavid du Colombier.CW path 140219b2ee8SDavid du Colombierhad been set as above, this would be equivalent to 141219b2ee8SDavid du Colombier.P1 142219b2ee8SDavid du Colombierecho . /bin 143219b2ee8SDavid du Colombier.P2 144219b2ee8SDavid du ColombierVariables may be subscripted by numbers or lists of numbers, 145219b2ee8SDavid du Colombierlike this: 146219b2ee8SDavid du Colombier.P1 147219b2ee8SDavid du Colombierecho $path(2) 148219b2ee8SDavid du Colombierecho $path(2 1 2) 149219b2ee8SDavid du Colombier.P2 150219b2ee8SDavid du ColombierThese are equivalent to 151219b2ee8SDavid du Colombier.P1 152219b2ee8SDavid du Colombierecho /bin 153219b2ee8SDavid du Colombierecho /bin . /bin 154219b2ee8SDavid du Colombier.P2 155219b2ee8SDavid du ColombierThere can be no space separating the variable's name from the 156219b2ee8SDavid du Colombierleft parenthesis; otherwise, the subscript would be considered 157219b2ee8SDavid du Colombiera separate parenthesized list. 158219b2ee8SDavid du Colombier.PP 159219b2ee8SDavid du ColombierThe number of strings in a variable can be determined by the 160219b2ee8SDavid du Colombier.CW $# 161219b2ee8SDavid du Colombieroperator. For example, 162219b2ee8SDavid du Colombier.P1 163219b2ee8SDavid du Colombierecho $#path 164219b2ee8SDavid du Colombier.P2 165219b2ee8SDavid du Colombierwould print 2 for this example. 166219b2ee8SDavid du Colombier.PP 167219b2ee8SDavid du ColombierThe following two assignments are subtly different: 168219b2ee8SDavid du Colombier.P1 169219b2ee8SDavid du Colombierempty=() 170219b2ee8SDavid du Colombiernull='' 171219b2ee8SDavid du Colombier.P2 172219b2ee8SDavid du ColombierThe first sets 173219b2ee8SDavid du Colombier.CW empty 174219b2ee8SDavid du Colombierto a list containing no strings. 175219b2ee8SDavid du ColombierThe second sets 176219b2ee8SDavid du Colombier.CW null 177219b2ee8SDavid du Colombierto a list containing a single string, 178219b2ee8SDavid du Colombierbut the string contains no characters. 179219b2ee8SDavid du Colombier.PP 180219b2ee8SDavid du ColombierAlthough these may seem like more or less 181219b2ee8SDavid du Colombierthe same thing (in Bourne's shell, they are 182219b2ee8SDavid du Colombierindistinguishable), they behave differently 183219b2ee8SDavid du Colombierin almost all circumstances. 184219b2ee8SDavid du ColombierAmong other things 185219b2ee8SDavid du Colombier.P1 186219b2ee8SDavid du Colombierecho $#empty 187219b2ee8SDavid du Colombier.P2 188219b2ee8SDavid du Colombierprints 0, whereas 189219b2ee8SDavid du Colombier.P1 190219b2ee8SDavid du Colombierecho $#null 191219b2ee8SDavid du Colombier.P2 192219b2ee8SDavid du Colombierprints 1. 193219b2ee8SDavid du Colombier.PP 194219b2ee8SDavid du ColombierAll variables that have never been set have the value 195219b2ee8SDavid du Colombier.CW () . 196219b2ee8SDavid du Colombier.PP 197219b2ee8SDavid du ColombierOccasionally, it is convenient to treat a variable's value 198219b2ee8SDavid du Colombieras a single string. The elements of a string are concatenated 199219b2ee8SDavid du Colombierinto a single string, with spaces between the elements, by 200219b2ee8SDavid du Colombierthe 201219b2ee8SDavid du Colombier.CW $" 202219b2ee8SDavid du Colombieroperator. 203219b2ee8SDavid du ColombierThus, if we set 204219b2ee8SDavid du Colombier.P1 205219b2ee8SDavid du Colombierlist=(How now brown cow) 206219b2ee8SDavid du Colombierstring=$"list 207219b2ee8SDavid du Colombier.P2 208219b2ee8SDavid du Colombierthen both 209219b2ee8SDavid du Colombier.P1 210219b2ee8SDavid du Colombierecho $list 211219b2ee8SDavid du Colombier.P2 212219b2ee8SDavid du Colombierand 213219b2ee8SDavid du Colombier.P1 214219b2ee8SDavid du Colombierecho $string 215219b2ee8SDavid du Colombier.P2 216219b2ee8SDavid du Colombiercause the same output, viz: 217219b2ee8SDavid du Colombier.P1 218219b2ee8SDavid du ColombierHow now brown cow 219219b2ee8SDavid du Colombier.P2 220219b2ee8SDavid du Colombierbut 221219b2ee8SDavid du Colombier.P1 222219b2ee8SDavid du Colombierecho $#list $#string 223219b2ee8SDavid du Colombier.P2 224219b2ee8SDavid du Colombierwill output 225219b2ee8SDavid du Colombier.P1 226219b2ee8SDavid du Colombier4 1 227219b2ee8SDavid du Colombier.P2 228219b2ee8SDavid du Colombierbecause 229219b2ee8SDavid du Colombier.CW $list 230219b2ee8SDavid du Colombierhas four members, but 231219b2ee8SDavid du Colombier.CW $string 232219b2ee8SDavid du Colombierhas a single member, with three spaces separating its words. 233219b2ee8SDavid du Colombier.NH 234219b2ee8SDavid du ColombierArguments 235219b2ee8SDavid du Colombier.PP 236219b2ee8SDavid du ColombierWhen 237219b2ee8SDavid du Colombier.I rc 238219b2ee8SDavid du Colombieris reading its input from a file, the file has access 239219b2ee8SDavid du Colombierto the arguments supplied on 240219b2ee8SDavid du Colombier.I rc 's 241219b2ee8SDavid du Colombiercommand line. The variable 242219b2ee8SDavid du Colombier.CW $* 243219b2ee8SDavid du Colombierinitially has the list of arguments assigned to it. 244219b2ee8SDavid du ColombierThe names 245219b2ee8SDavid du Colombier.CW $1 , 246219b2ee8SDavid du Colombier.CW $2 , 247219b2ee8SDavid du Colombieretc. are synonyms for 248219b2ee8SDavid du Colombier.CW $*(1) , 249219b2ee8SDavid du Colombier.CW $*(2) , 250219b2ee8SDavid du Colombieretc. 251219b2ee8SDavid du ColombierIn addition, 252219b2ee8SDavid du Colombier.CW $0 253219b2ee8SDavid du Colombieris the name of the file from which 254219b2ee8SDavid du Colombier.I rc 's 255219b2ee8SDavid du Colombierinput is being read. 256219b2ee8SDavid du Colombier.NH 257219b2ee8SDavid du ColombierConcatenation 258219b2ee8SDavid du Colombier.PP 259219b2ee8SDavid du Colombier.I Rc 260219b2ee8SDavid du Colombierhas a string concatenation operator, the caret 261219b2ee8SDavid du Colombier.CW ^ , 262219b2ee8SDavid du Colombierto build arguments out of pieces. 263219b2ee8SDavid du Colombier.P1 264219b2ee8SDavid du Colombierecho hully^gully 265219b2ee8SDavid du Colombier.P2 266219b2ee8SDavid du Colombieris exactly equivalent to 267219b2ee8SDavid du Colombier.P1 268219b2ee8SDavid du Colombierecho hullygully 269219b2ee8SDavid du Colombier.P2 270219b2ee8SDavid du ColombierSuppose variable 271219b2ee8SDavid du Colombier.CW i 272219b2ee8SDavid du Colombiercontains the name of a command. 273219b2ee8SDavid du ColombierThen 274219b2ee8SDavid du Colombier.P1 275219b2ee8SDavid du Colombiervc $i^.c 276219b2ee8SDavid du Colombiervl -o $1 $i^.v 277219b2ee8SDavid du Colombier.P2 278219b2ee8SDavid du Colombiermight compile the command's source code, leaving the 279219b2ee8SDavid du Colombierresult in the appropriate file. 280219b2ee8SDavid du Colombier.PP 281219b2ee8SDavid du ColombierConcatenation distributes over lists. The following 282219b2ee8SDavid du Colombier.P1 283219b2ee8SDavid du Colombierecho (a b c)^(1 2 3) 284219b2ee8SDavid du Colombiersrc=(main subr io) 285219b2ee8SDavid du Colombiercc $src^.c 286219b2ee8SDavid du Colombier.P2 287219b2ee8SDavid du Colombierare equivalent to 288219b2ee8SDavid du Colombier.P1 289219b2ee8SDavid du Colombierecho a1 b2 c3 290219b2ee8SDavid du Colombiercc main.c subr.c io.c 291219b2ee8SDavid du Colombier.P2 292219b2ee8SDavid du ColombierIn detail, the rule is: if both operands of 293219b2ee8SDavid du Colombier.CW ^ 294219b2ee8SDavid du Colombierare lists of the same non-zero number of strings, they are concatenated 295219b2ee8SDavid du Colombierpairwise. Otherwise, if one of the operands is a single string, 296219b2ee8SDavid du Colombierit is concatenated with each member of the other operand in turn. 297219b2ee8SDavid du ColombierAny other combination of operands is an error. 298219b2ee8SDavid du Colombier.NH 299219b2ee8SDavid du ColombierFree carets 300219b2ee8SDavid du Colombier.PP 301219b2ee8SDavid du ColombierUser demand has dictated that 302219b2ee8SDavid du Colombier.I rc 303219b2ee8SDavid du Colombierinsert carets in certain places, to make the syntax 304219b2ee8SDavid du Colombierlook more like the Bourne shell. For example, this: 305219b2ee8SDavid du Colombier.P1 306219b2ee8SDavid du Colombiercc -$flags $stems.c 307219b2ee8SDavid du Colombier.P2 308219b2ee8SDavid du Colombieris equivalent to 309219b2ee8SDavid du Colombier.P1 310219b2ee8SDavid du Colombiercc -^$flags $stems^.c 311219b2ee8SDavid du Colombier.P2 312219b2ee8SDavid du ColombierIn general, 313219b2ee8SDavid du Colombier.I rc 314219b2ee8SDavid du Colombierwill insert 315219b2ee8SDavid du Colombier.CW ^ 316219b2ee8SDavid du Colombierbetween two arguments that are not separated by white space. 317219b2ee8SDavid du ColombierSpecifically, whenever one of 318219b2ee8SDavid du Colombier.CW "$'` 319219b2ee8SDavid du Colombierfollows a quoted or unquoted word, or an unquoted word follows 320219b2ee8SDavid du Colombiera quoted word with no intervening blanks or tabs, an implicit 321219b2ee8SDavid du Colombier.CW ^ 322219b2ee8SDavid du Colombieris inserted between the two. If an unquoted word immediately following a 323219b2ee8SDavid du Colombier.CW $ 324219b2ee8SDavid du Colombiercontains a character other than an alphanumeric, underscore or 325219b2ee8SDavid du Colombier.CW * , 326219b2ee8SDavid du Colombiera 327219b2ee8SDavid du Colombier.CW ^ 328219b2ee8SDavid du Colombieris inserted before the first such character. 329219b2ee8SDavid du Colombier.NH 330219b2ee8SDavid du ColombierCommand substitution 331219b2ee8SDavid du Colombier.PP 332219b2ee8SDavid du ColombierIt is often useful to build an argument list from the output of a command. 333219b2ee8SDavid du Colombier.I Rc 334219b2ee8SDavid du Colombierallows a command, enclosed in braces and preceded by a left quote, 335219b2ee8SDavid du Colombier.CW "`{...}" , 336219b2ee8SDavid du Colombieranywhere that an argument is required. The command is executed and its 337219b2ee8SDavid du Colombierstandard output captured. 338219b2ee8SDavid du ColombierThe characters stored in the variable 339219b2ee8SDavid du Colombier.CW ifs 340219b2ee8SDavid du Colombierare used to split the output into arguments. 341219b2ee8SDavid du ColombierFor example, 342219b2ee8SDavid du Colombier.P1 343219b2ee8SDavid du Colombiercat `{ls -tr|sed 10q} 344219b2ee8SDavid du Colombier.P2 345219b2ee8SDavid du Colombierwill concatenate the ten oldest files in the current directory in temporal order, given the 346219b2ee8SDavid du Colombierdefault 347219b2ee8SDavid du Colombier.CW ifs 348219b2ee8SDavid du Colombiersetting of space, tab, and newline. 349219b2ee8SDavid du Colombier.NH 350219b2ee8SDavid du ColombierPipeline branching 351219b2ee8SDavid du Colombier.PP 352219b2ee8SDavid du ColombierThe normal pipeline notation is general enough for almost all cases. 353219b2ee8SDavid du ColombierVery occasionally it is useful to have pipelines that are not linear. 354219b2ee8SDavid du ColombierPipeline topologies more general than trees can require arbitrarily large pipe buffers, 355219b2ee8SDavid du Colombieror worse, can cause deadlock. 356219b2ee8SDavid du Colombier.I Rc 357219b2ee8SDavid du Colombierhas syntax for some kinds of non-linear but treelike pipelines. 358219b2ee8SDavid du ColombierFor example, 359219b2ee8SDavid du Colombier.P1 360219b2ee8SDavid du Colombier cmp <{old} <{new} 361219b2ee8SDavid du Colombier.P2 362219b2ee8SDavid du Colombierwill regression-test a new version of a command. 363219b2ee8SDavid du Colombier.CW < 364219b2ee8SDavid du Colombieror 365219b2ee8SDavid du Colombier.CW > 366219b2ee8SDavid du Colombierfollowed by a command in braces causes the command to be run with 367219b2ee8SDavid du Colombierits standard output or input attached to a pipe. The parent command 368219b2ee8SDavid du Colombier.CW cmp "" ( 369219b2ee8SDavid du Colombierin the example) 370219b2ee8SDavid du Colombieris started with the other end of the pipe attached to some file descriptor 371219b2ee8SDavid du Colombieror other, and with an argument that will connect to the pipe when opened 372219b2ee8SDavid du Colombier(e.g., 373219b2ee8SDavid du Colombier.CW /dev/fd/6 ). 374219b2ee8SDavid du ColombierSome commands are unprepared to deal with input files that turn out not to be seekable. 375219b2ee8SDavid du ColombierFor example 376219b2ee8SDavid du Colombier.CW diff 377219b2ee8SDavid du Colombierneeds to read its input twice. 378219b2ee8SDavid du Colombier.NH 379219b2ee8SDavid du ColombierExit status 380219b2ee8SDavid du Colombier.PP 381219b2ee8SDavid du ColombierWhen a command exits it returns status to the program that executed it. 382219b2ee8SDavid du ColombierOn Plan 9 status is a character string describing an error condition. 383219b2ee8SDavid du ColombierOn normal termination it is empty. 384219b2ee8SDavid du Colombier.PP 385219b2ee8SDavid du Colombier.I Rc 386219b2ee8SDavid du Colombiercaptures command exit status in the variable 387219b2ee8SDavid du Colombier.CW $status . 388219b2ee8SDavid du ColombierFor a simple command the value of 389219b2ee8SDavid du Colombier.CW $status 390219b2ee8SDavid du Colombieris just as described above. For a pipeline 391219b2ee8SDavid du Colombier.CW $status 392219b2ee8SDavid du Colombieris set to the concatenation of the statuses of the pipeline components with 393219b2ee8SDavid du Colombier.CW | 394219b2ee8SDavid du Colombiercharacters for separators. 395219b2ee8SDavid du Colombier.PP 396219b2ee8SDavid du Colombier.I Rc 397219b2ee8SDavid du Colombierhas a several kinds of control flow, 398219b2ee8SDavid du Colombiermany of them conditioned by the status returned from previously 399219b2ee8SDavid du Colombierexecuted commands. Any 400219b2ee8SDavid du Colombier.CW $status 401219b2ee8SDavid du Colombiercontaining only 402219b2ee8SDavid du Colombier.CW 0 's 403219b2ee8SDavid du Colombierand 404219b2ee8SDavid du Colombier.CW | 's 405219b2ee8SDavid du Colombierhas boolean value 406219b2ee8SDavid du Colombier.I true . 407219b2ee8SDavid du ColombierAny other status is 408219b2ee8SDavid du Colombier.I false . 409219b2ee8SDavid du Colombier.NH 410219b2ee8SDavid du ColombierCommand grouping 411219b2ee8SDavid du Colombier.PP 412219b2ee8SDavid du ColombierA sequence of commands enclosed in 413219b2ee8SDavid du Colombier.CW {} 414219b2ee8SDavid du Colombiermay be used anywhere a command is required. 415219b2ee8SDavid du ColombierFor example: 416219b2ee8SDavid du Colombier.P1 417219b2ee8SDavid du Colombier{sleep 3600;echo 'Time''s up!'}& 418219b2ee8SDavid du Colombier.P2 419219b2ee8SDavid du Colombierwill wait an hour in the background, then print a message. 420219b2ee8SDavid du ColombierWithout the braces, 421219b2ee8SDavid du Colombier.P1 422219b2ee8SDavid du Colombiersleep 3600;echo 'Time''s up!'& 423219b2ee8SDavid du Colombier.P2 424219b2ee8SDavid du Colombierwould lock up the terminal for an hour, 425219b2ee8SDavid du Colombierthen print the message in the background. 426219b2ee8SDavid du Colombier.NH 427219b2ee8SDavid du ColombierControl flow \(em \f(CWfor\fP 428219b2ee8SDavid du Colombier.PP 429219b2ee8SDavid du ColombierA command may be executed once for each member of a list 430219b2ee8SDavid du Colombierby typing, for example: 431219b2ee8SDavid du Colombier.P1 432219b2ee8SDavid du Colombierfor(i in printf scanf putchar) look $i /usr/td/lib/dw.dat 433219b2ee8SDavid du Colombier.P2 434219b2ee8SDavid du ColombierThis looks for each of the words 435219b2ee8SDavid du Colombier.CW printf , 436219b2ee8SDavid du Colombier.CW scanf 437219b2ee8SDavid du Colombierand 438219b2ee8SDavid du Colombier.CW putchar 439219b2ee8SDavid du Colombierin the given file. 440219b2ee8SDavid du ColombierThe general form is 441219b2ee8SDavid du Colombier.P1 442219b2ee8SDavid du Colombierfor(\fIname\fP in \fIlist\fP) \fIcommand\fP 443219b2ee8SDavid du Colombier.P2 444219b2ee8SDavid du Colombieror 445219b2ee8SDavid du Colombier.P1 446219b2ee8SDavid du Colombierfor(\fIname\fP) \fIcommand\fP 447219b2ee8SDavid du Colombier.P2 448219b2ee8SDavid du ColombierIn the first case 449219b2ee8SDavid du Colombier.I command 450219b2ee8SDavid du Colombieris executed once for each member of 451219b2ee8SDavid du Colombier.I list 452219b2ee8SDavid du Colombierwith that member assigned to variable 453219b2ee8SDavid du Colombier.I name . 454219b2ee8SDavid du ColombierIf the clause 455219b2ee8SDavid du Colombier.CW in "" `` 456219b2ee8SDavid du Colombier.I list '' 457219b2ee8SDavid du Colombieris missing, 458219b2ee8SDavid du Colombier.CW in "" `` 459219b2ee8SDavid du Colombier.CW $* '' 460219b2ee8SDavid du Colombieris assumed. 461219b2ee8SDavid du Colombier.NH 462219b2ee8SDavid du ColombierConditional execution \(em \f(CWif\fP 463219b2ee8SDavid du Colombier.PP 464219b2ee8SDavid du Colombier.I Rc 465219b2ee8SDavid du Colombieralso provides a general if-statement. For example: 466219b2ee8SDavid du Colombier.P1 467219b2ee8SDavid du Colombierfor(i in *.c) if(cpp $i >/tmp/$i) vc /tmp/$i 468219b2ee8SDavid du Colombier.P2 469219b2ee8SDavid du Colombierruns the C compiler on each C source program that 470219b2ee8SDavid du Colombiercpp processes without error. 471219b2ee8SDavid du ColombierAn `if not' statement provides a two-tailed conditional. 472219b2ee8SDavid du ColombierFor example: 473219b2ee8SDavid du Colombier.P1 474219b2ee8SDavid du Colombierfor(i){ 475219b2ee8SDavid du Colombier if(test -f /tmp/$i) echo $i already in /tmp 476219b2ee8SDavid du Colombier if not cp $i /tmp 477219b2ee8SDavid du Colombier} 478219b2ee8SDavid du Colombier.P2 479219b2ee8SDavid du ColombierThis loops over each file in 480219b2ee8SDavid du Colombier.CW $* , 481219b2ee8SDavid du Colombiercopying to 482219b2ee8SDavid du Colombier.CW /tmp 483219b2ee8SDavid du Colombierthose that do not already appear there, and 484219b2ee8SDavid du Colombierprinting a message for those that do. 485219b2ee8SDavid du Colombier.NH 486219b2ee8SDavid du ColombierControl flow \(em \f(CWwhile\fP 487219b2ee8SDavid du Colombier.PP 488219b2ee8SDavid du Colombier.I Rc 's 489219b2ee8SDavid du Colombierwhile statement looks like this: 490219b2ee8SDavid du Colombier.P1 491219b2ee8SDavid du Colombierwhile(newer subr.v subr.c) sleep 5 492219b2ee8SDavid du Colombier.P2 493219b2ee8SDavid du ColombierThis waits until 494219b2ee8SDavid du Colombier.CW subr.v 495219b2ee8SDavid du Colombieris newer than 496219b2ee8SDavid du Colombier.CW subr.c , 497219b2ee8SDavid du Colombierpresumably because the C compiler finished with it. 498219b2ee8SDavid du Colombier.PP 499219b2ee8SDavid du ColombierIf the controlling command is empty, the loop will not terminate. 500219b2ee8SDavid du ColombierThus, 501219b2ee8SDavid du Colombier.P1 502219b2ee8SDavid du Colombierwhile() echo y 503219b2ee8SDavid du Colombier.P2 504219b2ee8SDavid du Colombieremulates the 505219b2ee8SDavid du Colombier.I yes 506219b2ee8SDavid du Colombiercommand. 507219b2ee8SDavid du Colombier.NH 508219b2ee8SDavid du ColombierControl flow \(em \f(CWswitch\fP 509219b2ee8SDavid du Colombier.PP 510219b2ee8SDavid du Colombier.I Rc 511219b2ee8SDavid du Colombierprovides a switch statement to do pattern-matching on 512219b2ee8SDavid du Colombierarbitrary strings. Its general form is 513219b2ee8SDavid du Colombier.P1 514219b2ee8SDavid du Colombierswitch(\fIword\fP){ 515219b2ee8SDavid du Colombiercase \fIpattern ...\fP 516219b2ee8SDavid du Colombier \fIcommands\fP 517219b2ee8SDavid du Colombiercase \fIpattern ...\fP 518219b2ee8SDavid du Colombier \fIcommands\fP 519219b2ee8SDavid du Colombier\&... 520219b2ee8SDavid du Colombier} 521219b2ee8SDavid du Colombier.P2 522219b2ee8SDavid du Colombier.I Rc 523219b2ee8SDavid du Colombierattempts to match the word against the patterns in each case statement in turn. 524219b2ee8SDavid du ColombierPatterns are the same as for filename matching, except that 525219b2ee8SDavid du Colombier.CW / 526219b2ee8SDavid du Colombierand 527219b2ee8SDavid du Colombier.CW . 528219b2ee8SDavid du Colombierand 529219b2ee8SDavid du Colombier.CW .. 530219b2ee8SDavid du Colombierneed not be matched explicitly. 531219b2ee8SDavid du Colombier.PP 532219b2ee8SDavid du ColombierIf any pattern matches, the 533219b2ee8SDavid du Colombiercommands following that case up to 534219b2ee8SDavid du Colombierthe next case (or the end of the switch) 535219b2ee8SDavid du Colombierare executed, and execution of the switch 536219b2ee8SDavid du Colombieris complete. For example, 537219b2ee8SDavid du Colombier.P1 538219b2ee8SDavid du Colombierswitch($#*){ 539219b2ee8SDavid du Colombiercase 1 540219b2ee8SDavid du Colombier cat >>$1 541219b2ee8SDavid du Colombiercase 2 542219b2ee8SDavid du Colombier cat >>$2 <$1 543219b2ee8SDavid du Colombiercase * 544219b2ee8SDavid du Colombier echo 'Usage: append [from] to' 545219b2ee8SDavid du Colombier} 546219b2ee8SDavid du Colombier.P2 547219b2ee8SDavid du Colombieris an append command. Called with one file argument, 548219b2ee8SDavid du Colombierit appends its standard input to the named file. With two, the 549219b2ee8SDavid du Colombierfirst is appended to the second. Any other number 550219b2ee8SDavid du Colombierelicits an error message. 551219b2ee8SDavid du Colombier.PP 552219b2ee8SDavid du ColombierThe built-in 553219b2ee8SDavid du Colombier.CW ~ 554219b2ee8SDavid du Colombiercommand also matches patterns, and is often more concise than a switch. 555219b2ee8SDavid du ColombierIts arguments are a string and a list of patterns. It sets 556219b2ee8SDavid du Colombier.CW $status 557219b2ee8SDavid du Colombierto true if and only if any of the patterns matches the string. 558219b2ee8SDavid du ColombierThe following example processes option arguments for the 559219b2ee8SDavid du Colombier.I man (1) 560219b2ee8SDavid du Colombiercommand: 561219b2ee8SDavid du Colombier.P1 562219b2ee8SDavid du Colombieropt=() 563219b2ee8SDavid du Colombierwhile(~ $1 -* [1-9] 10){ 564219b2ee8SDavid du Colombier switch($1){ 565219b2ee8SDavid du Colombier case [1-9] 10 566219b2ee8SDavid du Colombier sec=$1 secn=$1 567219b2ee8SDavid du Colombier case -f 568219b2ee8SDavid du Colombier c=f s=f 569219b2ee8SDavid du Colombier case -[qwnt] 570219b2ee8SDavid du Colombier cmd=$1 571219b2ee8SDavid du Colombier case -T* 572219b2ee8SDavid du Colombier T=$1 573219b2ee8SDavid du Colombier case -* 574219b2ee8SDavid du Colombier opt=($opt $1) 575219b2ee8SDavid du Colombier } 576219b2ee8SDavid du Colombier shift 577219b2ee8SDavid du Colombier} 578219b2ee8SDavid du Colombier.P2 579219b2ee8SDavid du Colombier.NH 580219b2ee8SDavid du ColombierFunctions 581219b2ee8SDavid du Colombier.PP 582219b2ee8SDavid du ColombierFunctions may be defined by typing 583219b2ee8SDavid du Colombier.P1 584219b2ee8SDavid du Colombierfn \fIname\fP { \fIcommands\fP } 585219b2ee8SDavid du Colombier.P2 586219b2ee8SDavid du ColombierSubsequently, whenever a command named 587219b2ee8SDavid du Colombier.I name 588219b2ee8SDavid du Colombieris encountered, the remainder of the command's 589219b2ee8SDavid du Colombierargument list will assigned to 590219b2ee8SDavid du Colombier.CW $* 591219b2ee8SDavid du Colombierand 592219b2ee8SDavid du Colombier.I rc 593219b2ee8SDavid du Colombierwill execute the 594219b2ee8SDavid du Colombier.I commands . 595219b2ee8SDavid du ColombierThe value of 596219b2ee8SDavid du Colombier.CW $* 597219b2ee8SDavid du Colombierwill be restored on completion. 598219b2ee8SDavid du ColombierFor example: 599219b2ee8SDavid du Colombier.P1 600219b2ee8SDavid du Colombierfn g { 601219b2ee8SDavid du Colombier grep $1 *.[hcyl] 602219b2ee8SDavid du Colombier} 603219b2ee8SDavid du Colombier.P2 604219b2ee8SDavid du Colombierdefines 605219b2ee8SDavid du Colombier.CI g " pattern 606219b2ee8SDavid du Colombierto look for occurrences of 607219b2ee8SDavid du Colombier.I pattern 608219b2ee8SDavid du Colombierin all program source files in the current directory. 609219b2ee8SDavid du Colombier.PP 610219b2ee8SDavid du ColombierFunction definitions are deleted by writing 611219b2ee8SDavid du Colombier.P1 612219b2ee8SDavid du Colombierfn \fIname\fP 613219b2ee8SDavid du Colombier.P2 614219b2ee8SDavid du Colombierwith no function body. 615219b2ee8SDavid du Colombier.NH 616219b2ee8SDavid du ColombierCommand execution 617219b2ee8SDavid du Colombier.PP 618219b2ee8SDavid du Colombier.I Rc 619219b2ee8SDavid du Colombierdoes one of several things to execute a simple command. 620219b2ee8SDavid du ColombierIf the command name is the name of a function defined using 621219b2ee8SDavid du Colombier.CW fn , 622219b2ee8SDavid du Colombierthe function is executed. 623219b2ee8SDavid du ColombierOtherwise, if it is the name of a built-in command, the 624219b2ee8SDavid du Colombierbuilt-in is executed directly by 625219b2ee8SDavid du Colombier.I rc . 626219b2ee8SDavid du ColombierOtherwise, directories mentioned in the variable 627219b2ee8SDavid du Colombier.CW $path 628219b2ee8SDavid du Colombierare searched until an executable file is found. 629219b2ee8SDavid du ColombierExtensive use of the 630219b2ee8SDavid du Colombier.CW $path 631219b2ee8SDavid du Colombiervariable is discouraged in Plan 9. Instead, use the default 632219b2ee8SDavid du Colombier.CW (. 633219b2ee8SDavid du Colombier.CW /bin) 634219b2ee8SDavid du Colombierand bind what you need into 635219b2ee8SDavid du Colombier.CW /bin . 636219b2ee8SDavid du Colombier.NH 637219b2ee8SDavid du ColombierBuilt-in commands 638219b2ee8SDavid du Colombier.PP 639219b2ee8SDavid du ColombierSeveral commands are executed internally by 640219b2ee8SDavid du Colombier.I rc 641219b2ee8SDavid du Colombierbecause they are difficult to implement otherwise. 642219b2ee8SDavid du Colombier.TP ". [-i] \fIfile ...\f(CW 643219b2ee8SDavid du ColombierExecute commands from 644219b2ee8SDavid du Colombier.I file . 645219b2ee8SDavid du Colombier.CW $* 646219b2ee8SDavid du Colombieris set for the duration to the reminder of the argument list following 647219b2ee8SDavid du Colombier.I file . 648219b2ee8SDavid du Colombier.CW $path 649219b2ee8SDavid du Colombieris used to search for 650219b2ee8SDavid du Colombier.I file . 651219b2ee8SDavid du ColombierOption 652219b2ee8SDavid du Colombier.CW -i 653219b2ee8SDavid du Colombierindicates interactive input \(em a prompt 654219b2ee8SDavid du Colombier(found in 655219b2ee8SDavid du Colombier.CW $prompt ) 656219b2ee8SDavid du Colombieris printed before each command is read. 657219b2ee8SDavid du Colombier.TP "builtin \fIcommand ...\f(CW 658219b2ee8SDavid du ColombierExecute 659219b2ee8SDavid du Colombier.I command 660219b2ee8SDavid du Colombieras usual except that any function named 661219b2ee8SDavid du Colombier.I command 662219b2ee8SDavid du Colombieris ignored. 663219b2ee8SDavid du ColombierFor example, 664219b2ee8SDavid du Colombier.P1 665219b2ee8SDavid du Colombierfn cd{ 666219b2ee8SDavid du Colombier builtin cd $* && pwd 667219b2ee8SDavid du Colombier} 668219b2ee8SDavid du Colombier.P2 669219b2ee8SDavid du Colombierdefines a replacement for the 670219b2ee8SDavid du Colombier.CW cd 671219b2ee8SDavid du Colombierbuilt-in (see below) that announces the full name of the new directory. 672219b2ee8SDavid du Colombier.TP "cd [\fIdir\f(CW] 673219b2ee8SDavid du ColombierChange the current directory to 674219b2ee8SDavid du Colombier.I dir . 675219b2ee8SDavid du ColombierThe default argument is 676219b2ee8SDavid du Colombier.CW $home . 677219b2ee8SDavid du Colombier.CW $cdpath 678219b2ee8SDavid du Colombieris a list of places in which to search for 679219b2ee8SDavid du Colombier.I dir . 680219b2ee8SDavid du Colombier.TP "eval [\fIarg ...\f(CW] 681219b2ee8SDavid du ColombierThe arguments are concatenated (separated by spaces) into a string, read as input to 682219b2ee8SDavid du Colombier.I rc , 683219b2ee8SDavid du Colombierand executed. For example, 684219b2ee8SDavid du Colombier.P1 685219b2ee8SDavid du Colombierx='$y' 686219b2ee8SDavid du Colombiery=Doody 687219b2ee8SDavid du Colombiereval echo Howdy, $x 688219b2ee8SDavid du Colombier.P2 689219b2ee8SDavid du Colombierwould echo 690219b2ee8SDavid du Colombier.P1 691219b2ee8SDavid du ColombierHowdy, Doody 692219b2ee8SDavid du Colombier.P2 693219b2ee8SDavid du Colombiersince the arguments of 694219b2ee8SDavid du Colombier.CW eval 695219b2ee8SDavid du Colombierwould be 696219b2ee8SDavid du Colombier.P1 697219b2ee8SDavid du Colombierecho Howdy, $y 698219b2ee8SDavid du Colombier.P2 699219b2ee8SDavid du Colombierafter substituting for 700219b2ee8SDavid du Colombier.CW $x . 701219b2ee8SDavid du Colombier.TP "exec [\fIcommand ...\f(CW] 702219b2ee8SDavid du Colombier.I Rc 703219b2ee8SDavid du Colombierreplaces itself with the given 704219b2ee8SDavid du Colombier.I command . 705219b2ee8SDavid du ColombierThis is like a 706219b2ee8SDavid du Colombier.I goto 707219b2ee8SDavid du Colombier\(em 708219b2ee8SDavid du Colombier.I rc 709219b2ee8SDavid du Colombierdoes not wait for the command to exit, and does not return to read any more commands. 710219b2ee8SDavid du Colombier.TP "exit [\fIstatus\f(CW] 711219b2ee8SDavid du Colombier.I Rc 712219b2ee8SDavid du Colombierexits immediately with the given status. If none is given, the current value of 713219b2ee8SDavid du Colombier.CW $status 714219b2ee8SDavid du Colombieris used. 715219b2ee8SDavid du Colombier.TP "flag \fIf\f(CW [+-] 716219b2ee8SDavid du ColombierThis command manipulates and tests the command line flags (described below). 717219b2ee8SDavid du Colombier.P1 718219b2ee8SDavid du Colombierflag \fIf\f(CW + 719219b2ee8SDavid du Colombier.P2 720219b2ee8SDavid du Colombiersets flag 721219b2ee8SDavid du Colombier.I f . 722219b2ee8SDavid du Colombier.P1 723219b2ee8SDavid du Colombierflag \fIf\f(CW - 724219b2ee8SDavid du Colombier.P2 725219b2ee8SDavid du Colombierclears flag 726219b2ee8SDavid du Colombier.I f . 727219b2ee8SDavid du Colombier.P1 728219b2ee8SDavid du Colombierflag \fIf\f(CW 729219b2ee8SDavid du Colombier.P2 730219b2ee8SDavid du Colombiertests flag 731219b2ee8SDavid du Colombier.I f , 732219b2ee8SDavid du Colombiersetting 733219b2ee8SDavid du Colombier.CW $status 734219b2ee8SDavid du Colombierappropriately. 735219b2ee8SDavid du ColombierThus 736219b2ee8SDavid du Colombier.P1 737219b2ee8SDavid du Colombierif(flag x) flag v + 738219b2ee8SDavid du Colombier.P2 739219b2ee8SDavid du Colombiersets the 740219b2ee8SDavid du Colombier.CW -v 741219b2ee8SDavid du Colombierflag if the 742219b2ee8SDavid du Colombier.CW -x 743219b2ee8SDavid du Colombierflag is already set. 744219b2ee8SDavid du Colombier.TP "rfork [nNeEsfF] 745219b2ee8SDavid du ColombierThis uses the Plan 9 746219b2ee8SDavid du Colombier.I rfork 747219b2ee8SDavid du Colombiersystem entry to put 748219b2ee8SDavid du Colombier.I rc 749219b2ee8SDavid du Colombierinto a new process group with the following attributes: 750219b2ee8SDavid du Colombier.TS 751219b2ee8SDavid du Colombierbox; 752219b2ee8SDavid du Colombierl l l 753219b2ee8SDavid du ColombierlfCW l l. 754219b2ee8SDavid du ColombierFlag Name Function 755219b2ee8SDavid du Colombier_ 756219b2ee8SDavid du Colombiern RFNAMEG Make a copy of the parent's name space 757219b2ee8SDavid du ColombierN RFCNAMEG Start with a new, empty name space 758219b2ee8SDavid du Colombiere RFENVG Make a copy of the parent's environment 759219b2ee8SDavid du ColombierE RFCENVG Start with a new, empty environment 760219b2ee8SDavid du Colombiers RFNOTEG Make a new note group 761219b2ee8SDavid du Colombierf RFFDG Make a copy of the parent's file descriptor space 762219b2ee8SDavid du ColombierF RFCFDG Make a new, empty file descriptor space 763219b2ee8SDavid du Colombier.TE 764219b2ee8SDavid du ColombierSection 765219b2ee8SDavid du Colombier.I fork (2) 766219b2ee8SDavid du Colombierof the Programmer's Manual describes these attributes in more detail. 767219b2ee8SDavid du Colombier.TP "shift [\fIn\f(CW] 768219b2ee8SDavid du ColombierDelete the first 769219b2ee8SDavid du Colombier.I n 770219b2ee8SDavid du Colombier(default 1) elements of 771219b2ee8SDavid du Colombier.CW $* . 772219b2ee8SDavid du Colombier.TP "wait [\fIpid\fP] 773219b2ee8SDavid du ColombierWait for the process with the given 774219b2ee8SDavid du Colombier.I pid 775219b2ee8SDavid du Colombierto exit. If no 776219b2ee8SDavid du Colombier.I pid 777219b2ee8SDavid du Colombieris given, all outstanding processes are waited for. 778219b2ee8SDavid du Colombier.TP "whatis \fIname ...\f(CW 779219b2ee8SDavid du ColombierPrint the value of each 780219b2ee8SDavid du Colombier.I name 781219b2ee8SDavid du Colombierin a form suitable for input to 782219b2ee8SDavid du Colombier.I rc . 783219b2ee8SDavid du ColombierThe output is an assignment to a variable, the definition of a function, 784219b2ee8SDavid du Colombiera call to 785219b2ee8SDavid du Colombier.CW builtin 786219b2ee8SDavid du Colombierfor a built-in command, or the path name of a binary program. 787219b2ee8SDavid du ColombierFor example, 788219b2ee8SDavid du Colombier.P1 789219b2ee8SDavid du Colombierwhatis path g cd who 790219b2ee8SDavid du Colombier.P2 791219b2ee8SDavid du Colombiermight print 792219b2ee8SDavid du Colombier.P1 793219b2ee8SDavid du Colombierpath=(. /bin) 794219b2ee8SDavid du Colombierfn g {gre -e $1 *.[hycl]} 795219b2ee8SDavid du Colombierbuiltin cd 796219b2ee8SDavid du Colombier/bin/who 797219b2ee8SDavid du Colombier.P2 79839734e7eSDavid du Colombier.TP "~ \fIsubject pattern ...\f(CW 799219b2ee8SDavid du ColombierThe 800219b2ee8SDavid du Colombier.I subject 801219b2ee8SDavid du Colombieris matched against each 802219b2ee8SDavid du Colombier.I pattern 803219b2ee8SDavid du Colombierin turn. On a match, 804219b2ee8SDavid du Colombier.CW $status 805219b2ee8SDavid du Colombieris set to true. 806219b2ee8SDavid du ColombierOtherwise, it is set to 807219b2ee8SDavid du Colombier.CW "'no match'" . 808219b2ee8SDavid du ColombierPatterns are the same as for filename matching. 809219b2ee8SDavid du ColombierThe 810219b2ee8SDavid du Colombier.I patterns 811219b2ee8SDavid du Colombierare not subjected to filename replacement before the 812219b2ee8SDavid du Colombier.CW ~ 813219b2ee8SDavid du Colombiercommand is executed, so they need not be enclosed in 814219b2ee8SDavid du Colombierquotation marks, unless of course, a literal match for 815219b2ee8SDavid du Colombier.CW * 816219b2ee8SDavid du Colombier.CW [ 817219b2ee8SDavid du Colombieror 818219b2ee8SDavid du Colombier.CW ? 819219b2ee8SDavid du Colombieris required. 820219b2ee8SDavid du ColombierFor example 821219b2ee8SDavid du Colombier.P1 822219b2ee8SDavid du Colombier~ $1 ? 823219b2ee8SDavid du Colombier.P2 824219b2ee8SDavid du Colombiermatches any single character, whereas 825219b2ee8SDavid du Colombier.P1 826219b2ee8SDavid du Colombier~ $1 '?' 827219b2ee8SDavid du Colombier.P2 828219b2ee8SDavid du Colombieronly matches a literal question mark. 829219b2ee8SDavid du Colombier.NH 830219b2ee8SDavid du ColombierAdvanced I/O Redirection 831219b2ee8SDavid du Colombier.PP 832219b2ee8SDavid du Colombier.I Rc 833219b2ee8SDavid du Colombierallows redirection of file descriptors other than 0 and 1 834219b2ee8SDavid du Colombier(standard input and output) by specifying the file descriptor 835219b2ee8SDavid du Colombierin square brackets 836219b2ee8SDavid du Colombier.CW "[ ] 837219b2ee8SDavid du Colombierafter the 838219b2ee8SDavid du Colombier.CW < 839219b2ee8SDavid du Colombieror 840219b2ee8SDavid du Colombier.CW > . 841219b2ee8SDavid du ColombierFor example, 842219b2ee8SDavid du Colombier.P1 843219b2ee8SDavid du Colombiervc junk.c >[2]junk.diag 844219b2ee8SDavid du Colombier.P2 845219b2ee8SDavid du Colombiersaves the compiler's diagnostics from standard error in 846219b2ee8SDavid du Colombier.CW junk.diag . 847219b2ee8SDavid du Colombier.PP 848219b2ee8SDavid du ColombierFile descriptors may be replaced by a copy, in the sense of 849219b2ee8SDavid du Colombier.I dup (2), 850219b2ee8SDavid du Colombierof an already-open file by typing, for example 851219b2ee8SDavid du Colombier.P1 852219b2ee8SDavid du Colombiervc junk.c >[2=1] 853219b2ee8SDavid du Colombier.P2 854219b2ee8SDavid du ColombierThis replaces file descriptor 2 with a copy of file descriptor 1. 855219b2ee8SDavid du ColombierIt is more useful in conjunction with other redirections, like this 856219b2ee8SDavid du Colombier.P1 857219b2ee8SDavid du Colombiervc junk.c >junk.out >[2=1] 858219b2ee8SDavid du Colombier.P2 859219b2ee8SDavid du ColombierRedirections are evaluated from left to right, so this redirects 860219b2ee8SDavid du Colombierfile descriptor 1 to 861219b2ee8SDavid du Colombier.CW junk.out , 862219b2ee8SDavid du Colombierthen points file descriptor 2 at the same file. 863219b2ee8SDavid du ColombierBy contrast, 864219b2ee8SDavid du Colombier.P1 865219b2ee8SDavid du Colombiervc junk.c >[2=1] >junk.out 866219b2ee8SDavid du Colombier.P2 867219b2ee8SDavid du Colombierredirects file descriptor 2 to a copy of file descriptor 1 868219b2ee8SDavid du Colombier(presumably the terminal), and then directs file descriptor 1 869219b2ee8SDavid du Colombierto a file. In the first case, standard and diagnostic output 870219b2ee8SDavid du Colombierwill be intermixed in 871219b2ee8SDavid du Colombier.CW junk.out . 872219b2ee8SDavid du ColombierIn the second, diagnostic output will appear on the terminal, 873219b2ee8SDavid du Colombierand standard output will be sent to the file. 874219b2ee8SDavid du Colombier.PP 875219b2ee8SDavid du ColombierFile descriptors may be closed by using the duplication notation 876219b2ee8SDavid du Colombierwith an empty right-hand side. 877219b2ee8SDavid du ColombierFor example, 878219b2ee8SDavid du Colombier.P1 879219b2ee8SDavid du Colombiervc junk.c >[2=] 880219b2ee8SDavid du Colombier.P2 881219b2ee8SDavid du Colombierwill discard diagnostics from the compilation. 882219b2ee8SDavid du Colombier.PP 883219b2ee8SDavid du ColombierArbitrary file descriptors may be sent through 884219b2ee8SDavid du Colombiera pipe by typing, for example, 885219b2ee8SDavid du Colombier.P1 886219b2ee8SDavid du Colombiervc junk.c |[2] grep -v '^$' 887219b2ee8SDavid du Colombier.P2 888219b2ee8SDavid du ColombierThis deletes blank lines 889219b2ee8SDavid du Colombierfrom the C compiler's error output. Note that the output 890219b2ee8SDavid du Colombierof 891219b2ee8SDavid du Colombier.CW grep 892219b2ee8SDavid du Colombierstill appears on file descriptor 1. 893219b2ee8SDavid du Colombier.PP 894219b2ee8SDavid du ColombierOccasionally you may wish to connect the input side of 895219b2ee8SDavid du Colombiera pipe to some file descriptor other than zero. 896219b2ee8SDavid du ColombierThe notation 897219b2ee8SDavid du Colombier.P1 898219b2ee8SDavid du Colombiercmd1 |[5=19] cmd2 899219b2ee8SDavid du Colombier.P2 900219b2ee8SDavid du Colombiercreates a pipeline with 901219b2ee8SDavid du Colombier.CW cmd1 's 902219b2ee8SDavid du Colombierfile descriptor 5 connected through a pipe to 903219b2ee8SDavid du Colombier.CW cmd2 's 904219b2ee8SDavid du Colombierfile descriptor 19. 905219b2ee8SDavid du Colombier.NH 906219b2ee8SDavid du ColombierHere documents 907219b2ee8SDavid du Colombier.PP 908219b2ee8SDavid du Colombier.I Rc 909219b2ee8SDavid du Colombierprocedures may include data, called ``here documents'', 910219b2ee8SDavid du Colombierto be provided as input to commands, as in this version of the 911219b2ee8SDavid du Colombier.I tel 912219b2ee8SDavid du Colombiercommand 913219b2ee8SDavid du Colombier.P1 914219b2ee8SDavid du Colombierfor(i) grep $i <<! 915219b2ee8SDavid du Colombier\&... 916219b2ee8SDavid du Colombiertor 2T-402 2912 917219b2ee8SDavid du Colombierkevin 2C-514 2842 918219b2ee8SDavid du Colombierbill 2C-562 7214 919219b2ee8SDavid du Colombier\&... 920219b2ee8SDavid du Colombier! 921219b2ee8SDavid du Colombier.P2 922219b2ee8SDavid du ColombierA here document is introduced by the redirection symbol 923219b2ee8SDavid du Colombier.CW << , 924219b2ee8SDavid du Colombierfollowed by an arbitrary EOF marker 925219b2ee8SDavid du Colombier.CW ! "" ( 926219b2ee8SDavid du Colombierin the example). Lines following the command, 927219b2ee8SDavid du Colombierup to a line containing only the EOF marker are saved 928219b2ee8SDavid du Colombierin a temporary file that is connected to the command's 929219b2ee8SDavid du Colombierstandard input when it is run. 930219b2ee8SDavid du Colombier.PP 931219b2ee8SDavid du Colombier.I Rc 932219b2ee8SDavid du Colombierdoes variable substitution in here documents. The following command: 933219b2ee8SDavid du Colombier.P1 934219b2ee8SDavid du Colombiered $3 <<EOF 935219b2ee8SDavid du Colombierg/$1/s//$2/g 936219b2ee8SDavid du Colombierw 937219b2ee8SDavid du ColombierEOF 938219b2ee8SDavid du Colombier.P2 939219b2ee8SDavid du Colombierchanges all occurrences of 940219b2ee8SDavid du Colombier.CW $1 941219b2ee8SDavid du Colombierto 942219b2ee8SDavid du Colombier.CW $2 943219b2ee8SDavid du Colombierin file 944219b2ee8SDavid du Colombier.CW $3 . 945219b2ee8SDavid du ColombierTo include a literal 946219b2ee8SDavid du Colombier.CW $ 947219b2ee8SDavid du Colombierin a here document, type 948219b2ee8SDavid du Colombier.CW $$ . 949219b2ee8SDavid du ColombierIf the name of a variable is followed immediately by 950219b2ee8SDavid du Colombier.CW ^ , 951219b2ee8SDavid du Colombierthe caret is deleted. 952219b2ee8SDavid du Colombier.PP 953219b2ee8SDavid du ColombierVariable substitution can be entirely suppressed by enclosing 954219b2ee8SDavid du Colombierthe EOF marker following 955219b2ee8SDavid du Colombier.CW << 956219b2ee8SDavid du Colombierin quotation marks, as in 957219b2ee8SDavid du Colombier.CW <<'EOF' . 958219b2ee8SDavid du Colombier.PP 959219b2ee8SDavid du ColombierHere documents may be provided on file descriptors other than 0 by typing, for example, 960219b2ee8SDavid du Colombier.P1 961219b2ee8SDavid du Colombiercmd <<[4]End 962219b2ee8SDavid du Colombier\&... 963219b2ee8SDavid du ColombierEnd 964219b2ee8SDavid du Colombier.P2 965219b2ee8SDavid du Colombier.PP 966219b2ee8SDavid du ColombierIf a here document appears within a compound block, the contents of the document 967219b2ee8SDavid du Colombiermust be after the whole block: 968219b2ee8SDavid du Colombier.P1 969219b2ee8SDavid du Colombierfor(i in $*){ 970219b2ee8SDavid du Colombier mail $i <<EOF 971219b2ee8SDavid du Colombier} 972219b2ee8SDavid du Colombierwords to live by 973219b2ee8SDavid du ColombierEOF 974219b2ee8SDavid du Colombier.P2 975219b2ee8SDavid du Colombier.NH 976219b2ee8SDavid du ColombierCatching Notes 977219b2ee8SDavid du Colombier.PP 978219b2ee8SDavid du Colombier.I Rc 979219b2ee8SDavid du Colombierscripts normally terminate when an interrupt is received from the terminal. 980219b2ee8SDavid du ColombierA function with the name of a UNIX signal, in lower case, is defined in the usual way, 981219b2ee8SDavid du Colombierbut called when 982219b2ee8SDavid du Colombier.I rc 983219b2ee8SDavid du Colombierreceives the corresponding note. 984219b2ee8SDavid du ColombierThe 985219b2ee8SDavid du Colombier.I notify (2) 986219b2ee8SDavid du Colombiersection of the Programmer's Manual discusses notes in some detail. 987219b2ee8SDavid du ColombierNotes of interest are: 988219b2ee8SDavid du Colombier.TP sighup 989219b2ee8SDavid du ColombierThe note was `hangup'. 990219b2ee8SDavid du ColombierPlan 9 sends this when the terminal has disconnected from 991219b2ee8SDavid du Colombier.I rc . 992219b2ee8SDavid du Colombier.TP sigint 993219b2ee8SDavid du ColombierThe note was `interrupt', usually sent when 994219b2ee8SDavid du Colombierthe interrupt character (ASCII DEL) is typed on the terminal. 995219b2ee8SDavid du Colombier.TP sigterm 996219b2ee8SDavid du ColombierThe note was `kill', normally sent by 997219b2ee8SDavid du Colombier.I kill (1). 998219b2ee8SDavid du Colombier.TP sigexit 999219b2ee8SDavid du ColombierAn artificial note sent when 1000219b2ee8SDavid du Colombier.I rc 1001219b2ee8SDavid du Colombieris about to exit. 1002219b2ee8SDavid du Colombier.PP 1003219b2ee8SDavid du ColombierAs an example, 1004219b2ee8SDavid du Colombier.P1 1005219b2ee8SDavid du Colombierfn sigint{ 1006219b2ee8SDavid du Colombier rm /tmp/junk 1007219b2ee8SDavid du Colombier exit 1008219b2ee8SDavid du Colombier} 1009219b2ee8SDavid du Colombier.P2 1010219b2ee8SDavid du Colombiersets a trap for the keyboard interrupt that 1011219b2ee8SDavid du Colombierremoves a temporary file before exiting. 1012219b2ee8SDavid du Colombier.PP 1013219b2ee8SDavid du ColombierNotes will be ignored if the note routine is set to 1014219b2ee8SDavid du Colombier.CW {} . 1015219b2ee8SDavid du ColombierSignals revert to their default behavior when their handlers' 1016219b2ee8SDavid du Colombierdefinitions are deleted. 1017219b2ee8SDavid du Colombier.NH 1018219b2ee8SDavid du ColombierEnvironment 1019219b2ee8SDavid du Colombier.PP 1020219b2ee8SDavid du ColombierThe environment is a list of name-value pairs made available to 1021219b2ee8SDavid du Colombierexecuting binaries. 1022219b2ee8SDavid du ColombierOn Plan 9, the environment is stored in a file system named 1023219b2ee8SDavid du Colombier.CW #e , 1024219b2ee8SDavid du Colombiernormally mounted on 1025219b2ee8SDavid du Colombier.CW /env . 1026219b2ee8SDavid du ColombierThe value of each variable is stored in a separate file, with components 1027219b2ee8SDavid du Colombierterminated by zero bytes. 1028219b2ee8SDavid du Colombier(The file system is 1029219b2ee8SDavid du Colombiermaintained entirely in core, so no disk or network access is involved.) 1030219b2ee8SDavid du ColombierThe contents of 1031219b2ee8SDavid du Colombier.CW /env 1032219b2ee8SDavid du Colombierare shared on a per-process group basis \(mi when a new process group is 1033219b2ee8SDavid du Colombiercreated it effectively attaches 1034219b2ee8SDavid du Colombier.CW /env 1035219b2ee8SDavid du Colombierto a new file system initialized with a copy of the old one. 1036219b2ee8SDavid du ColombierA consequence of this organization is that commands can change environment 1037219b2ee8SDavid du Colombierentries and see the changes reflected in 1038219b2ee8SDavid du Colombier.I rc . 1039219b2ee8SDavid du Colombier.PP 1040219b2ee8SDavid du ColombierFunctions also appear in the environment, named by prefixing 1041219b2ee8SDavid du Colombier.CW fn# 1042219b2ee8SDavid du Colombierto their names, like 1043219b2ee8SDavid du Colombier.CW /env/fn#roff . 1044219b2ee8SDavid du Colombier.NH 1045219b2ee8SDavid du ColombierLocal Variables 1046219b2ee8SDavid du Colombier.PP 1047219b2ee8SDavid du ColombierIt is often useful to set a variable for the duration 1048219b2ee8SDavid du Colombierof a single command. An assignment followed by a command 1049219b2ee8SDavid du Colombierhas this effect. For example 1050219b2ee8SDavid du Colombier.P1 1051219b2ee8SDavid du Colombiera=global 1052219b2ee8SDavid du Colombiera=local echo $a 1053219b2ee8SDavid du Colombierecho $a 1054219b2ee8SDavid du Colombier.P2 1055219b2ee8SDavid du Colombierwill print 1056219b2ee8SDavid du Colombier.P1 1057219b2ee8SDavid du Colombierlocal 1058219b2ee8SDavid du Colombierglobal 1059219b2ee8SDavid du Colombier.P2 1060219b2ee8SDavid du ColombierThis works even for compound commands, like 1061219b2ee8SDavid du Colombier.P1 1062219b2ee8SDavid du Colombierf=/fairly/long/file/name { 1063219b2ee8SDavid du Colombier { wc $f; spell $f; diff $f.old $f } | 1064219b2ee8SDavid du Colombier pr -h 'Facts about '$f | lp -dfn 1065219b2ee8SDavid du Colombier} 1066219b2ee8SDavid du Colombier.P2 1067219b2ee8SDavid du Colombier.NH 1068219b2ee8SDavid du ColombierExamples \(em \fIcd, pwd\fP 1069219b2ee8SDavid du Colombier.PP 1070219b2ee8SDavid du ColombierHere is a pair of functions that provide 1071219b2ee8SDavid du Colombierenhanced versions of the standard 1072219b2ee8SDavid du Colombier.CW cd 1073219b2ee8SDavid du Colombierand 1074219b2ee8SDavid du Colombier.CW pwd 1075219b2ee8SDavid du Colombiercommands. (Thanks to Rob Pike for these.) 1076219b2ee8SDavid du Colombier.P1 1077219b2ee8SDavid du Colombierps1='% ' # default prompt 1078219b2ee8SDavid du Colombiertab=' ' # a tab character 1079219b2ee8SDavid du Colombierfn cd{ 1080219b2ee8SDavid du Colombier builtin cd $1 && 1081219b2ee8SDavid du Colombier switch($#*){ 1082219b2ee8SDavid du Colombier case 0 1083219b2ee8SDavid du Colombier dir=$home 1084219b2ee8SDavid du Colombier prompt=($ps1 $tab) 1085219b2ee8SDavid du Colombier case * 1086219b2ee8SDavid du Colombier switch($1) 1087219b2ee8SDavid du Colombier case /* 1088219b2ee8SDavid du Colombier dir=$1 1089219b2ee8SDavid du Colombier prompt=(`{basename `{pwd}}^$ps1 $tab) 1090219b2ee8SDavid du Colombier case */* ..* 1091219b2ee8SDavid du Colombier dir=() 1092219b2ee8SDavid du Colombier prompt=(`{basename `{pwd}}^$ps1 $tab) 1093219b2ee8SDavid du Colombier case * 1094219b2ee8SDavid du Colombier dir=() 1095219b2ee8SDavid du Colombier prompt=($1^$ps1 $tab) 1096219b2ee8SDavid du Colombier } 1097219b2ee8SDavid du Colombier } 1098219b2ee8SDavid du Colombier} 1099219b2ee8SDavid du Colombierfn pwd{ 1100219b2ee8SDavid du Colombier if(~ $#dir 0) 1101219b2ee8SDavid du Colombier dir=`{/bin/pwd} 1102219b2ee8SDavid du Colombier echo $dir 1103219b2ee8SDavid du Colombier} 1104219b2ee8SDavid du Colombier.P2 1105219b2ee8SDavid du ColombierFunction 1106219b2ee8SDavid du Colombier.CW pwd 1107219b2ee8SDavid du Colombieris a version of the standard 1108219b2ee8SDavid du Colombier.CW pwd 1109219b2ee8SDavid du Colombierthat caches its value in variable 1110219b2ee8SDavid du Colombier.CW $dir , 1111219b2ee8SDavid du Colombierbecause the genuine 1112219b2ee8SDavid du Colombier.CW pwd 1113219b2ee8SDavid du Colombiercan be quite slow to execute. 11147dd7cddfSDavid du Colombier(Recent versions of Plan 9 have very fast implementations of 11157dd7cddfSDavid du Colombier.CW pwd , 11167dd7cddfSDavid du Colombierreducing the advantage of the 11177dd7cddfSDavid du Colombier.CW pwd 11187dd7cddfSDavid du Colombierfunction.) 1119219b2ee8SDavid du Colombier.PP 1120219b2ee8SDavid du ColombierFunction 1121219b2ee8SDavid du Colombier.CW cd 1122219b2ee8SDavid du Colombiercalls the 1123219b2ee8SDavid du Colombier.CW cd 1124219b2ee8SDavid du Colombierbuilt-in, and checks that it was successful. 1125219b2ee8SDavid du ColombierIf so, it sets 1126219b2ee8SDavid du Colombier.CW $dir 1127219b2ee8SDavid du Colombierand 1128219b2ee8SDavid du Colombier.CW $prompt . 1129219b2ee8SDavid du ColombierThe prompt will include the last component of the 1130219b2ee8SDavid du Colombiercurrent directory (except in the home directory, 1131219b2ee8SDavid du Colombierwhere it will be null), and 1132219b2ee8SDavid du Colombier.CW $dir 1133219b2ee8SDavid du Colombierwill be reset either to the correct value or to 1134219b2ee8SDavid du Colombier.CW () , 1135219b2ee8SDavid du Colombierso that the 1136219b2ee8SDavid du Colombier.CW pwd 1137219b2ee8SDavid du Colombierfunction will work correctly. 1138219b2ee8SDavid du Colombier.NH 1139219b2ee8SDavid du ColombierExamples \(em \fIman\fP 1140219b2ee8SDavid du Colombier.PP 1141219b2ee8SDavid du ColombierThe 1142219b2ee8SDavid du Colombier.I man 1143219b2ee8SDavid du Colombiercommand prints pages of the Programmer's Manual. 1144219b2ee8SDavid du ColombierIt is called, for example, as 1145219b2ee8SDavid du Colombier.P1 1146219b2ee8SDavid du Colombierman 2 sinh 1147219b2ee8SDavid du Colombierman rc 1148219b2ee8SDavid du Colombierman -t cat 1149219b2ee8SDavid du Colombier.P2 1150219b2ee8SDavid du ColombierIn the first case, the page for 1151219b2ee8SDavid du Colombier.I sinh 1152219b2ee8SDavid du Colombierin section 2 is printed. 1153219b2ee8SDavid du ColombierIn the second case, the manual page for 1154219b2ee8SDavid du Colombier.I rc 1155219b2ee8SDavid du Colombieris printed. Since no manual section is specified, 1156219b2ee8SDavid du Colombierall sections are searched for the page, and it is found 1157219b2ee8SDavid du Colombierin section 1. 1158219b2ee8SDavid du ColombierIn the third case, the page for 1159219b2ee8SDavid du Colombier.I cat 1160219b2ee8SDavid du Colombieris typeset (the 1161219b2ee8SDavid du Colombier.CW -t 1162219b2ee8SDavid du Colombieroption). 1163219b2ee8SDavid du Colombier.P1 1164219b2ee8SDavid du Colombiercd /sys/man || { 1165219b2ee8SDavid du Colombier echo $0: No manual! >[1=2] 1166219b2ee8SDavid du Colombier exit 1 1167219b2ee8SDavid du Colombier} 1168219b2ee8SDavid du ColombierNT=n # default nroff 1169219b2ee8SDavid du Colombiers='*' # section, default try all 1170219b2ee8SDavid du Colombierfor(i) switch($i){ 1171219b2ee8SDavid du Colombiercase -t 1172219b2ee8SDavid du Colombier NT=t 1173219b2ee8SDavid du Colombiercase -n 1174219b2ee8SDavid du Colombier NT=n 1175219b2ee8SDavid du Colombiercase -* 1176219b2ee8SDavid du Colombier echo Usage: $0 '[-nt] [section] page ...' >[1=2] 1177219b2ee8SDavid du Colombier exit 1 1178219b2ee8SDavid du Colombiercase [1-9] 10 1179219b2ee8SDavid du Colombier s=$i 1180219b2ee8SDavid du Colombiercase * 1181219b2ee8SDavid du Colombier eval 'pages='$s/$i 1182219b2ee8SDavid du Colombier for(page in $pages){ 1183219b2ee8SDavid du Colombier if(test -f $page) 1184219b2ee8SDavid du Colombier $NT^roff -man $page 1185219b2ee8SDavid du Colombier if not 1186219b2ee8SDavid du Colombier echo $0: $i not found >[1=2] 1187219b2ee8SDavid du Colombier } 1188219b2ee8SDavid du Colombier} 1189219b2ee8SDavid du Colombier.P2 1190219b2ee8SDavid du ColombierNote the use of 1191219b2ee8SDavid du Colombier.CW eval 1192219b2ee8SDavid du Colombierto make a list of candidate manual pages. 1193219b2ee8SDavid du ColombierWithout 1194219b2ee8SDavid du Colombier.CW eval , 1195219b2ee8SDavid du Colombierthe 1196219b2ee8SDavid du Colombier.CW * 1197219b2ee8SDavid du Colombierstored in 1198219b2ee8SDavid du Colombier.CW $s 1199219b2ee8SDavid du Colombierwould not trigger filename matching 1200219b2ee8SDavid du Colombier\(em it's enclosed in quotation marks, 1201219b2ee8SDavid du Colombierand even if it weren't, it would be expanded 1202219b2ee8SDavid du Colombierwhen assigned to 1203219b2ee8SDavid du Colombier.CW $s . 1204219b2ee8SDavid du ColombierEval causes its arguments 1205219b2ee8SDavid du Colombierto be re-processed by 1206219b2ee8SDavid du Colombier.I rc 's 1207219b2ee8SDavid du Colombierparser and interpreter, effectively delaying 1208219b2ee8SDavid du Colombierevaluation of the 1209219b2ee8SDavid du Colombier.CW * 1210219b2ee8SDavid du Colombieruntil the assignment to 1211219b2ee8SDavid du Colombier.CW $pages . 1212219b2ee8SDavid du Colombier.NH 1213219b2ee8SDavid du ColombierExamples \(em \fIholmdel\fP 1214219b2ee8SDavid du Colombier.PP 1215219b2ee8SDavid du ColombierThe following 1216219b2ee8SDavid du Colombier.I rc 1217219b2ee8SDavid du Colombierscript plays the deceptively simple game 1218219b2ee8SDavid du Colombier.I holmdel , 1219219b2ee8SDavid du Colombierin which the players alternately name Bell Labs locations, 1220219b2ee8SDavid du Colombierthe winner being the first to mention Holmdel. 1221219b2ee8SDavid du Colombier.KF 1222219b2ee8SDavid du Colombier.P1 1223219b2ee8SDavid du Colombiert=/tmp/holmdel$pid 1224219b2ee8SDavid du Colombierfn read{ 1225219b2ee8SDavid du Colombier $1=`{awk '{print;exit}'} 1226219b2ee8SDavid du Colombier} 1227219b2ee8SDavid du Colombierifs=' 1228219b2ee8SDavid du Colombier\&' # just a newline 1229219b2ee8SDavid du Colombierfn sigexit sigint sigquit sighup{ 1230219b2ee8SDavid du Colombier rm -f $t 1231219b2ee8SDavid du Colombier exit 1232219b2ee8SDavid du Colombier} 1233219b2ee8SDavid du Colombiercat <<'!' >$t 1234219b2ee8SDavid du ColombierAllentown 1235219b2ee8SDavid du ColombierAtlanta 1236219b2ee8SDavid du ColombierCedar Crest 1237219b2ee8SDavid du ColombierChester 1238219b2ee8SDavid du ColombierColumbus 1239219b2ee8SDavid du ColombierElmhurst 1240219b2ee8SDavid du ColombierFullerton 1241219b2ee8SDavid du ColombierHolmdel 1242219b2ee8SDavid du ColombierIndian Hill 1243219b2ee8SDavid du ColombierMerrimack Valley 1244219b2ee8SDavid du ColombierMorristown 1245219b2ee8SDavid du ColombierNeptune 1246219b2ee8SDavid du ColombierPiscataway 1247219b2ee8SDavid du ColombierReading 1248219b2ee8SDavid du ColombierShort Hills 1249219b2ee8SDavid du ColombierSouth Plainfield 1250219b2ee8SDavid du ColombierSummit 1251219b2ee8SDavid du ColombierWhippany 1252219b2ee8SDavid du ColombierWest Long Branch 1253219b2ee8SDavid du Colombier! 1254219b2ee8SDavid du Colombierwhile(){ 1255219b2ee8SDavid du Colombier lab=`{fortune $t} 1256219b2ee8SDavid du Colombier echo $lab 1257219b2ee8SDavid du Colombier if(~ $lab Holmdel){ 1258219b2ee8SDavid du Colombier echo You lose. 1259219b2ee8SDavid du Colombier exit 1260219b2ee8SDavid du Colombier } 1261219b2ee8SDavid du Colombier while(read lab; ! grep -i -s $lab $t) echo No such location. 1262219b2ee8SDavid du Colombier if(~ $lab [hH]olmdel){ 1263219b2ee8SDavid du Colombier echo You win. 1264219b2ee8SDavid du Colombier exit 1265219b2ee8SDavid du Colombier } 1266219b2ee8SDavid du Colombier} 1267219b2ee8SDavid du Colombier.P2 1268219b2ee8SDavid du Colombier.KE 1269219b2ee8SDavid du Colombier.PP 1270219b2ee8SDavid du ColombierThis script is worth describing in detail 1271219b2ee8SDavid du Colombier(rather, it would be if it weren't so silly.) 1272219b2ee8SDavid du Colombier.PP 1273219b2ee8SDavid du ColombierVariable 1274219b2ee8SDavid du Colombier.CW $t 1275219b2ee8SDavid du Colombieris an abbreviation for the name of a temporary file. 1276219b2ee8SDavid du ColombierIncluding 1277219b2ee8SDavid du Colombier.CW $pid , 1278219b2ee8SDavid du Colombierinitialized by 1279219b2ee8SDavid du Colombier.I rc 1280219b2ee8SDavid du Colombierto its process-id, 1281219b2ee8SDavid du Colombierin the names of temporary files insures that their 1282219b2ee8SDavid du Colombiernames won't collide, in case more than one instance 1283219b2ee8SDavid du Colombierof the script is running at a time. 1284219b2ee8SDavid du Colombier.PP 1285219b2ee8SDavid du ColombierFunction 1286219b2ee8SDavid du Colombier.CW read 's 1287219b2ee8SDavid du Colombierargument is the name of a variable into which a 1288219b2ee8SDavid du Colombierline gathered from standard input is read. 1289219b2ee8SDavid du Colombier.CW $ifs 1290219b2ee8SDavid du Colombieris set to just a newline. Thus 1291219b2ee8SDavid du Colombier.CW read 's 1292219b2ee8SDavid du Colombierinput is not split apart at spaces, but the terminating 1293219b2ee8SDavid du Colombiernewline is deleted. 1294219b2ee8SDavid du Colombier.PP 1295219b2ee8SDavid du ColombierA handler is set to catch 1296219b2ee8SDavid du Colombier.CW sigint , 1297219b2ee8SDavid du Colombier.CW sigquit , 1298219b2ee8SDavid du Colombierand 1299219b2ee8SDavid du Colombier.CW sighup, 1300219b2ee8SDavid du Colombierand the artificial 1301219b2ee8SDavid du Colombier.CW sigexit 1302219b2ee8SDavid du Colombiersignal. It just removes the temporary file and exits. 1303219b2ee8SDavid du Colombier.PP 1304219b2ee8SDavid du ColombierThe temporary file is initialized from a here 1305219b2ee8SDavid du Colombierdocument containing a list of Bell Labs locations, and 1306219b2ee8SDavid du Colombierthe main loop starts. 1307219b2ee8SDavid du Colombier.PP 1308219b2ee8SDavid du ColombierFirst, the program guesses a location (in 1309219b2ee8SDavid du Colombier.CW $lab ) 1310219b2ee8SDavid du Colombierusing the 1311219b2ee8SDavid du Colombier.CW fortune 1312219b2ee8SDavid du Colombierprogram to pick a random line from the location list. 1313219b2ee8SDavid du ColombierIt prints the location, and if it guessed Holmdel, prints 1314219b2ee8SDavid du Colombiera message and exits. 1315219b2ee8SDavid du Colombier.PP 1316219b2ee8SDavid du ColombierThen it uses the 1317219b2ee8SDavid du Colombier.CW read 1318219b2ee8SDavid du Colombierfunction to get lines from standard input and validity-check 1319219b2ee8SDavid du Colombierthem until it gets a legal name. 1320219b2ee8SDavid du ColombierNote that the condition part of a 1321219b2ee8SDavid du Colombier.CW while 1322219b2ee8SDavid du Colombiercan be a compound command. Only the exit status of the 1323219b2ee8SDavid du Colombierlast command in the sequence is checked. 1324219b2ee8SDavid du Colombier.PP 1325219b2ee8SDavid du ColombierAgain, if the result 1326219b2ee8SDavid du Colombieris Holmdel, it prints a message and exits. 1327219b2ee8SDavid du ColombierOtherwise it goes back to the top of the loop. 1328219b2ee8SDavid du Colombier.NH 1329219b2ee8SDavid du ColombierDesign Principles 1330219b2ee8SDavid du Colombier.PP 1331219b2ee8SDavid du Colombier.I Rc 1332219b2ee8SDavid du Colombierdraws heavily from Steve Bourne's 1333219b2ee8SDavid du Colombier.CW /bin/sh . 1334219b2ee8SDavid du ColombierAny successor of the Bourne shell is bound to 1335219b2ee8SDavid du Colombiersuffer in comparison. I have tried to fix its 1336219b2ee8SDavid du Colombierbest-acknowledged shortcomings and to simplify things 1337219b2ee8SDavid du Colombierwherever possible, usually by omitting inessential features. 1338219b2ee8SDavid du ColombierOnly when irresistibly tempted have I introduced novel ideas. 1339219b2ee8SDavid du ColombierObviously I have tinkered extensively with Bourne's syntax. 1340219b2ee8SDavid du Colombier.PP 1341219b2ee8SDavid du ColombierThe most important principle in 1342219b2ee8SDavid du Colombier.I rc 's 1343219b2ee8SDavid du Colombierdesign is that it's not a macro processor. Input is never 1344219b2ee8SDavid du Colombierscanned more than once by the lexical and syntactic analysis 1345219b2ee8SDavid du Colombiercode (except, of course, by the 1346219b2ee8SDavid du Colombier.CW eval 1347219b2ee8SDavid du Colombiercommand, whose 1348219b2ee8SDavid du Colombier.I "raison d'être 1349219b2ee8SDavid du Colombieris to break the rule). 1350219b2ee8SDavid du Colombier.PP 1351219b2ee8SDavid du ColombierBourne shell scripts can often be made 1352219b2ee8SDavid du Colombierto run wild by passing them arguments containing spaces. 1353219b2ee8SDavid du ColombierThese will be split into multiple arguments using 1354219b2ee8SDavid du Colombier.CW IFS , 1355219b2ee8SDavid du Colombieroften at inopportune times. 1356219b2ee8SDavid du ColombierIn 1357219b2ee8SDavid du Colombier.I rc , 1358219b2ee8SDavid du Colombiervalues of variables, including command line arguments, are not re-read 1359219b2ee8SDavid du Colombierwhen substituted into a command. 1360219b2ee8SDavid du ColombierArguments have presumably been scanned in the parent process, and ought 1361219b2ee8SDavid du Colombiernot to be re-read. 1362219b2ee8SDavid du Colombier.PP 1363219b2ee8SDavid du ColombierWhy does Bourne re-scan commands after variable substitution? 1364219b2ee8SDavid du ColombierHe needs to be able to store lists of arguments in variables whose values are 1365219b2ee8SDavid du Colombiercharacter strings. 1366219b2ee8SDavid du ColombierIf we eliminate re-scanning, we must change the type of variables, so that 1367219b2ee8SDavid du Colombierthey can explicitly carry lists of strings. 1368219b2ee8SDavid du Colombier.PP 1369219b2ee8SDavid du ColombierThis introduces some 1370219b2ee8SDavid du Colombierconceptual complications. We need a notation for lists of words. 1371219b2ee8SDavid du ColombierThere are two different kinds of concatenation, for strings \(em 1372219b2ee8SDavid du Colombier.CW $a^$b , 1373219b2ee8SDavid du Colombierand lists \(em 1374219b2ee8SDavid du Colombier.CW "($a $b)" . 1375219b2ee8SDavid du ColombierThe difference between 1376219b2ee8SDavid du Colombier.CW () 1377219b2ee8SDavid du Colombierand 1378219b2ee8SDavid du Colombier.CW '' 1379219b2ee8SDavid du Colombieris confusing to novices, 1380219b2ee8SDavid du Colombieralthough the distinction is arguably sensible \(em 1381219b2ee8SDavid du Colombiera null argument is not the same as no argument. 1382219b2ee8SDavid du Colombier.PP 1383219b2ee8SDavid du ColombierBourne also rescans input when doing command substitution. 1384219b2ee8SDavid du ColombierThis is because the text enclosed in back-quotes is not 1385219b2ee8SDavid du Colombiera string, but a command. Properly, it ought to 1386219b2ee8SDavid du Colombierbe parsed when the enclosing command is, but this makes 1387219b2ee8SDavid du Colombierit difficult to 1388219b2ee8SDavid du Colombierhandle nested command substitutions, like this: 1389219b2ee8SDavid du Colombier.P1 1390219b2ee8SDavid du Colombiersize=`wc -l \e`ls -t|sed 1q\e`` 1391219b2ee8SDavid du Colombier.P2 1392219b2ee8SDavid du ColombierThe inner back-quotes must be escaped 1393219b2ee8SDavid du Colombierto avoid terminating the outer command. 1394219b2ee8SDavid du ColombierThis can get much worse than the above example; 1395219b2ee8SDavid du Colombierthe number of 1396219b2ee8SDavid du Colombier.CW \e 's 1397219b2ee8SDavid du Colombierrequired is exponential in the nesting depth. 1398219b2ee8SDavid du Colombier.I Rc 1399219b2ee8SDavid du Colombierfixes this by making the backquote a unary operator 1400219b2ee8SDavid du Colombierwhose argument is a command, like this: 1401219b2ee8SDavid du Colombier.P1 1402219b2ee8SDavid du Colombiersize=`{wc -l `{ls -t|sed 1q}} 1403219b2ee8SDavid du Colombier.P2 1404219b2ee8SDavid du ColombierNo escapes are ever required, and the whole thing 1405219b2ee8SDavid du Colombieris parsed in one pass. 1406219b2ee8SDavid du Colombier.PP 1407219b2ee8SDavid du ColombierFor similar reasons 1408219b2ee8SDavid du Colombier.I rc 1409219b2ee8SDavid du Colombierdefines signal handlers as though they were functions, 1410219b2ee8SDavid du Colombierinstead of associating a string with each signal, as Bourne does, 1411219b2ee8SDavid du Colombierwith the attendant possibility of getting a syntax error message 1412219b2ee8SDavid du Colombierin response to typing the interrupt character. Since 1413219b2ee8SDavid du Colombier.I rc 1414219b2ee8SDavid du Colombierparses input when typed, it reports errors when you make them. 1415219b2ee8SDavid du Colombier.PP 1416219b2ee8SDavid du ColombierFor all this trouble, we gain substantial semantic simplifications. 1417219b2ee8SDavid du ColombierThere is no need for the distinction between 1418219b2ee8SDavid du Colombier.CW $* 1419219b2ee8SDavid du Colombierand 1420219b2ee8SDavid du Colombier.CW $@ . 1421219b2ee8SDavid du ColombierThere is no need for four types of quotation, nor the 1422219b2ee8SDavid du Colombierextremely complicated rules that govern them. In 1423219b2ee8SDavid du Colombier.I rc 1424219b2ee8SDavid du Colombieryou use quotation marks when you want a syntax character 1425219b2ee8SDavid du Colombierto appear in an argument, or an argument that is the empty string, 1426219b2ee8SDavid du Colombierand at no other time. 1427219b2ee8SDavid du Colombier.CW IFS 1428219b2ee8SDavid du Colombieris no longer used, except in the one case where it was indispensable: 1429219b2ee8SDavid du Colombierconverting command output into argument lists during command substitution. 1430219b2ee8SDavid du Colombier.PP 1431219b2ee8SDavid du ColombierThis also avoids an important UNIX security hole. 1432219b2ee8SDavid du ColombierIn UNIX, the 1433219b2ee8SDavid du Colombier.I system 1434219b2ee8SDavid du Colombierand 1435219b2ee8SDavid du Colombier.I popen 1436219b2ee8SDavid du Colombierfunctions call 1437219b2ee8SDavid du Colombier.CW /bin/sh 1438219b2ee8SDavid du Colombierto execute a command. It is impossible to use either 1439219b2ee8SDavid du Colombierof these routines with any assurance that the specified command will 1440219b2ee8SDavid du Colombierbe executed, even if the caller of 1441219b2ee8SDavid du Colombier.I system 1442219b2ee8SDavid du Colombieror 1443219b2ee8SDavid du Colombier.I popen 1444219b2ee8SDavid du Colombierspecifies a full path name for the command. This can be devastating 1445219b2ee8SDavid du Colombierif it occurs in a set-userid program. 1446219b2ee8SDavid du ColombierThe problem is that 1447219b2ee8SDavid du Colombier.CW IFS 1448219b2ee8SDavid du Colombieris used to split the command into words, so an attacker can just 1449219b2ee8SDavid du Colombierset 1450219b2ee8SDavid du Colombier.CW IFS=/ 1451219b2ee8SDavid du Colombierin his environment and leave a Trojan horse 1452219b2ee8SDavid du Colombiernamed 1453219b2ee8SDavid du Colombier.CW usr 1454219b2ee8SDavid du Colombieror 1455219b2ee8SDavid du Colombier.CW bin 1456219b2ee8SDavid du Colombierin the current working directory before running the privileged program. 1457219b2ee8SDavid du Colombier.I Rc 1458219b2ee8SDavid du Colombierfixes this by never rescanning input for any reason. 1459219b2ee8SDavid du Colombier.PP 1460219b2ee8SDavid du ColombierMost of the other differences between 1461219b2ee8SDavid du Colombier.I rc 1462219b2ee8SDavid du Colombierand the Bourne shell are not so serious. I eliminated Bourne's 1463219b2ee8SDavid du Colombierpeculiar forms of variable substitution, like 1464219b2ee8SDavid du Colombier.P1 1465219b2ee8SDavid du Colombierecho ${a=b} ${c-d} ${e?error} 1466219b2ee8SDavid du Colombier.P2 1467219b2ee8SDavid du Colombierbecause they are little used, redundant and easily 1468219b2ee8SDavid du Colombierexpressed in less abstruse terms. 1469219b2ee8SDavid du ColombierI deleted the builtins 1470219b2ee8SDavid du Colombier.CW export , 1471219b2ee8SDavid du Colombier.CW readonly , 1472219b2ee8SDavid du Colombier.CW break , 1473219b2ee8SDavid du Colombier.CW continue , 1474219b2ee8SDavid du Colombier.CW read , 1475219b2ee8SDavid du Colombier.CW return , 1476219b2ee8SDavid du Colombier.CW set , 1477219b2ee8SDavid du Colombier.CW times 1478219b2ee8SDavid du Colombierand 1479219b2ee8SDavid du Colombier.CW unset 1480219b2ee8SDavid du Colombierbecause they seem redundant or 1481219b2ee8SDavid du Colombieronly marginally useful. 1482219b2ee8SDavid du Colombier.PP 1483219b2ee8SDavid du ColombierWhere Bourne's syntax draws from Algol 68, 1484219b2ee8SDavid du Colombier.I rc 's 1485219b2ee8SDavid du Colombieris based on C or Awk. This is harder to defend. 1486219b2ee8SDavid du ColombierI believe that, for example 1487219b2ee8SDavid du Colombier.P1 1488219b2ee8SDavid du Colombierif(test -f junk) rm junk 1489219b2ee8SDavid du Colombier.P2 1490219b2ee8SDavid du Colombieris better syntax than 1491219b2ee8SDavid du Colombier.P1 1492219b2ee8SDavid du Colombierif test -f junk; then rm junk; fi 1493219b2ee8SDavid du Colombier.P2 1494219b2ee8SDavid du Colombierbecause it is less cluttered with keywords, 1495219b2ee8SDavid du Colombierit avoids the semicolons that Bourne requires 1496219b2ee8SDavid du Colombierin odd places, 1497219b2ee8SDavid du Colombierand the syntax characters better set off the 1498219b2ee8SDavid du Colombieractive parts of the command. 1499219b2ee8SDavid du Colombier.PP 1500219b2ee8SDavid du ColombierThe one bit of large-scale syntax that Bourne 1501219b2ee8SDavid du Colombierunquestionably does better than 1502219b2ee8SDavid du Colombier.I rc 1503219b2ee8SDavid du Colombieris the 1504219b2ee8SDavid du Colombier.CW if 1505219b2ee8SDavid du Colombierstatement with 1506219b2ee8SDavid du Colombier.CW "else 1507219b2ee8SDavid du Colombierclause. 1508219b2ee8SDavid du Colombier.I Rc 's 1509219b2ee8SDavid du Colombier.CW if 1510219b2ee8SDavid du Colombierhas no terminating 1511219b2ee8SDavid du Colombier.CW fi -like 1512219b2ee8SDavid du Colombierbracket. As a result, the parser cannot 1513219b2ee8SDavid du Colombiertell whether or not to expect an 1514219b2ee8SDavid du Colombier.CW "else 1515219b2ee8SDavid du Colombierclause without looking ahead in its input. 1516219b2ee8SDavid du ColombierThe problem is that after reading, for example 1517219b2ee8SDavid du Colombier.P1 1518219b2ee8SDavid du Colombierif(test -f junk) echo junk found 1519219b2ee8SDavid du Colombier.P2 1520219b2ee8SDavid du Colombierin interactive mode, 1521219b2ee8SDavid du Colombier.I rc 1522219b2ee8SDavid du Colombiercannot decide whether to execute it immediately and print 1523219b2ee8SDavid du Colombier.CW $prompt(1) , 1524219b2ee8SDavid du Colombieror to print 1525219b2ee8SDavid du Colombier.CW $prompt(2) 1526219b2ee8SDavid du Colombierand wait for the 1527219b2ee8SDavid du Colombier.CW "else 1528219b2ee8SDavid du Colombierto be typed. 1529219b2ee8SDavid du ColombierIn the Bourne shell, this is not a problem, because the 1530219b2ee8SDavid du Colombier.CW if 1531219b2ee8SDavid du Colombiercommand must end with 1532219b2ee8SDavid du Colombier.CW fi , 1533219b2ee8SDavid du Colombierregardless of whether it contains an 1534219b2ee8SDavid du Colombier.CW else 1535219b2ee8SDavid du Colombieror not. 1536219b2ee8SDavid du Colombier.PP 1537219b2ee8SDavid du Colombier.I Rc 's 1538219b2ee8SDavid du Colombieradmittedly feeble solution is to declare that the 1539219b2ee8SDavid du Colombier.CW else 1540219b2ee8SDavid du Colombierclause is a separate statement, with the semantic 1541219b2ee8SDavid du Colombierproviso that it must immediately follow an 1542219b2ee8SDavid du Colombier.CW if , 1543219b2ee8SDavid du Colombierand to call it 1544219b2ee8SDavid du Colombier.CW "if not 1545219b2ee8SDavid du Colombierrather than 1546219b2ee8SDavid du Colombier.CW else , 1547219b2ee8SDavid du Colombieras a reminder that something odd is going on. 1548219b2ee8SDavid du ColombierThe only noticeable consequence of this is that 1549219b2ee8SDavid du Colombierthe braces are required in the construction 1550219b2ee8SDavid du Colombier.P1 1551219b2ee8SDavid du Colombierfor(i){ 1552219b2ee8SDavid du Colombier if(test -f $i) echo $i found 1553219b2ee8SDavid du Colombier if not echo $i not found 1554219b2ee8SDavid du Colombier} 1555219b2ee8SDavid du Colombier.P2 1556219b2ee8SDavid du Colombierand that 1557219b2ee8SDavid du Colombier.I rc 1558219b2ee8SDavid du Colombierresolves the ``dangling else'' ambiguity in opposition 1559219b2ee8SDavid du Colombierto most people's expectations. 1560219b2ee8SDavid du Colombier.PP 1561219b2ee8SDavid du ColombierIt is remarkable that in the four most recent editions of the UNIX system 1562219b2ee8SDavid du Colombierprogrammer's manual the Bourne shell grammar described in the manual page 1563219b2ee8SDavid du Colombierdoes not admit the command 1564219b2ee8SDavid du Colombier.CW who|wc . 1565219b2ee8SDavid du ColombierThis is surely an oversight, but it suggests something darker: 1566219b2ee8SDavid du Colombiernobody really knows what the Bourne shell's grammar is. Even examination 1567219b2ee8SDavid du Colombierof the source code is little help. The parser is implemented by recursive 1568219b2ee8SDavid du Colombierdescent, but the routines corresponding to the syntactic categories all 1569219b2ee8SDavid du Colombierhave a flag argument that subtly changes their operation depending on the 1570219b2ee8SDavid du Colombiercontext. 1571219b2ee8SDavid du Colombier.I Rc 's 1572219b2ee8SDavid du Colombierparser is implemented using 1573219b2ee8SDavid du Colombier.I yacc , 1574219b2ee8SDavid du Colombierso I can say precisely what the grammar is. 1575219b2ee8SDavid du Colombier.NH 1576219b2ee8SDavid du ColombierAcknowledgements 1577219b2ee8SDavid du Colombier.PP 1578219b2ee8SDavid du ColombierRob Pike, Howard Trickey and other Plan 9 users have been insistent, incessant 1579219b2ee8SDavid du Colombiersources of good ideas and criticism. Some examples in this document are plagiarized 1580219b2ee8SDavid du Colombierfrom [Bourne], 1581219b2ee8SDavid du Colombieras are most of 1582219b2ee8SDavid du Colombier.I rc 's 1583219b2ee8SDavid du Colombiergood features. 1584219b2ee8SDavid du Colombier.NH 1585219b2ee8SDavid du ColombierReference 1586219b2ee8SDavid du Colombier.LP 1587219b2ee8SDavid du ColombierS. R. Bourne, 1588219b2ee8SDavid du ColombierUNIX Time-Sharing System: The UNIX Shell, 1589219b2ee8SDavid du ColombierBell System Technical Journal, Volume 57 number 6, July-August 1978 1590