summaryrefslogtreecommitdiff
path: root/bin/sh/USD.doc/t2
blob: d49747e901ddaceb2c8c73cb94b8b7ca1c2647ae (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
.\"	$NetBSD: t2,v 1.3 2010/08/22 02:19:07 perry Exp $
.\"
.\" Copyright (C) Caldera International Inc. 2001-2002.  All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions are
.\" met:
.\"
.\" Redistributions of source code and documentation must retain the above
.\" copyright notice, this list of conditions and the following
.\" disclaimer.
.\"
.\" Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" All advertising materials mentioning features or use of this software
.\" must display the following acknowledgment:
.\"
.\" This product includes software developed or owned by Caldera
.\" International, Inc.  Neither the name of Caldera International, Inc.
.\" nor the names of other contributors may be used to endorse or promote
.\" products derived from this software without specific prior written
.\" permission.
.\"
.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
.\" INTERNATIONAL, INC.  AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
.\" DISCLAIMED.  IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE
.\" FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
.\" BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
.\" OR OTHERWISE) RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
.\" IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.\"	@(#)t2	8.1 (Berkeley) 6/8/93
.\"
.SH
2.0\ Shell\ scripts
.LP
The shell may be used to read and execute commands
contained in a file.
For example,
.DS
	sh file [ args \*(ZZ ]
.DE
calls the shell to read commands from \fIfile.\fP
Such a file is called a \fIshell script.\fP
Arguments may be supplied with the call
and are referred to in \fIfile\fP
using the positional parameters
\fB$1, $2, \*(ZZ\|.\fR
.LP
For example, if the file \fIwg\fP contains
.DS
	who \*(VT grep $1
.DE
then
.DS
	sh wg fred
.DE
is equivalent to
.DS
	who \*(VT grep fred
.DE
.LP
UNIX files have three independent attributes,
\fIread,\fP \fIwrite\fP and \fIexecute.\fP
The UNIX command \fIchmod\fP(1) may be used
to make a file executable.
For example,
.DS
	chmod +x wg
.DE
will ensure that the file \fIwg\fP has execute status.
Following this, the command
.DS
	wg fred
.DE
is equivalent to
.DS
	sh wg fred
.DE
This allows shell scripts and other programs
to be used interchangeably.
In either case a new process is created to
run the command.
.LP
The `\fB#\fP' character is used as a comment character by the shell.
All characters following the `#' on a line are ignored.
.LP
A typical modern system has several different shells, some with differing
command syntax, and it is desirable to specify which one should be
invoked when an executable script is invoked.
If the special comment
.DS
	#!/\fIpath\fP/\fIto\fP/\fIinterpreter\fP
.DE
appears as the first line in a script, it is used to specify the
absolute pathname of the shell (or other interpreter) that should be
used to execute the file.
(Without such a line, \fB/bin/sh\fP is assumed.)
It is best if a script explicitly states
what shell it is intended for in this manner.
.LP
As well as providing names for the positional
parameters,
the number of positional parameters to a script
is available as \fB$#\|.\fP
The name of the file being executed
is available as \fB$0\|.\fP
.LP
A special shell parameter \fB$\*(ST\fP
is used to substitute for all positional parameters
except \fB$0\|.\fP
A typical use of this is to provide
some default arguments,
as in,
.DS
	nroff \(miT450 \(mims $\*(ST
.DE
which simply prepends some arguments
to those already given.
(The variable \fB$@\fP also expands to ``all positional
parameters'', but is subtly different when expanded inside quotes.
See section 3.5, below.)
.SH
2.1\ Control\ flow\ -\ for
.LP
A frequent use of shell scripts is to loop
through the arguments (\fB$1, $2, \*(ZZ\fR)
executing commands once for each argument.
An example of such a script is
\fItel\fP that searches the file
\fB/usr/share/telnos\fR
that contains lines of the form
.DS
	\*(ZZ
	fred mh0123
	bert mh0789
	\*(ZZ
.DE
The text of \fItel\fP is
.DS
	#!/bin/sh

	for i
	do
		grep $i /usr/share/telnos
	done
.DE
The command
.DS
	tel fred
.DE
prints those lines in \fB/usr/share/telnos\fR
that contain the string \fIfred\|.\fP
.DS
	tel fred bert
.DE
prints those lines containing \fIfred\fP
followed by those for \fIbert.\fP
.LP
The \fBfor\fP loop notation is recognized by the shell
and has the general form
.DS
	\fBfor\fR \fIname\fR \fBin\fR \fIw1 w2 \*(ZZ\fR
	\fBdo\fR \fIcommand-list\fR
	\fBdone\fR
.DE
A \fIcommand-list\fP is a sequence of one or more
simple commands separated or terminated by a newline or semicolon.
Furthermore, reserved words
like \fBdo\fP and \fBdone\fP are only
recognized following a newline or
semicolon.
\fIname\fP is a shell variable that is set
to the words \fIw1 w2 \*(ZZ\fR in turn each time the \fIcommand-list\fP
following \fBdo\fP
is executed.
If \fBin\fR \fIw1 w2 \*(ZZ\fR
is omitted then the loop
is executed once for each positional parameter;
that is, \fBin\fR \fI$\*(ST\fR is assumed.
.LP
Another example of the use of the \fBfor\fP
loop is the \fIcreate\fP command
whose text is
.DS
	for i do >$i; done
.DE
The command
.DS
	create alpha beta
.DE
ensures that two empty files
\fIalpha\fP and \fIbeta\fP exist
and are empty.
The notation \fI>file\fP may be used on its
own to create or clear the contents of a file.
Notice also that a semicolon (or newline) is required before \fBdone.\fP
.SH
2.2\ Control\ flow\ -\ case
.LP
A multiple way branch is provided for by the
\fBcase\fP notation.
For example,
.DS
	case $# in
	\*(Ca1)	cat \*(AP$1 ;;
	\*(Ca2)	cat \*(AP$2 <$1 ;;
	\*(Ca\*(ST)	echo \'usage: append [ from ] to\' ;;
	esac
.DE
is an \fIappend\fP command.
When called
with one argument as
.DS
	append file
.DE
\fB$#\fP is the string \fI1\fP and
the standard input is copied onto the
end of \fIfile\fP
using the \fIcat\fP command.
.DS
	append file1 file2
.DE
appends the contents of \fIfile1\fP
onto \fIfile2.\fP
If the number of arguments supplied to
\fIappend\fP is other than 1 or 2
then a message is printed indicating
proper usage.
.LP
The general form of the \fBcase\fP command
is
.DS
	\fBcase \fIword \fBin
	\*(Ca\fIpattern\|\fB)\ \fIcommand-list\fB\|;;
	\*(Ca\*(ZZ
	\fBesac\fR
.DE
The shell attempts to match
\fIword\fR with each \fIpattern,\fR
in the order in which the patterns
appear.
If a match is found the
associated \fIcommand-list\fP is
executed and execution
of the \fBcase\fP is complete.
Since \*(ST is the pattern that matches any
string it can be used for the default case.
.LP
A word of caution:
no check is made to ensure that only
one pattern matches
the case argument.
The first match found defines the set of commands
to be executed.
In the example below the commands following
the second \*(ST will never be executed.
.DS
	case $# in
	\*(Ca\*(ST) \*(ZZ ;;
	\*(Ca\*(ST) \*(ZZ ;;
	esac
.DE
.LP
Another example of the use of the \fBcase\fP
construction is to distinguish
between different forms
of an argument.
The following example is a fragment of a \fIcc\fP command.
.DS
	for i
	do case $i in
	\*(DC\(mi[ocs])	\*(ZZ ;;
	\*(DC\(mi\*(ST)	echo "unknown flag $i" ;;
	\*(DC\*(ST.c)	/lib/c0 $i \*(ZZ ;;
	\*(DC\*(ST)	echo "unexpected argument $i" ;;
	\*(DOesac
	done
.DE
.LP
To allow the same commands to be associated
with more than one pattern
the \fBcase\fP command provides
for alternative patterns
separated by a \*(VT\|.
For example,
.DS
	case $i in
	\*(Ca\(mix\*(VT\(miy)	\*(ZZ
	esac
.DE
is equivalent to
.DS
	case $i in
	\*(Ca\(mi[xy])	\*(ZZ
	esac
.DE
.LP
The usual quoting conventions apply
so that
.DS
	case $i in
	\*(Ca\\?)	\*(ZZ
.DE
will match the character \fB?\|.\fP
.SH
2.3\ Here\ documents
.LP
The shell script \fItel\fP
in section 2.1 uses the file \fB/usr/share/telnos\fR
to supply the data
for \fIgrep.\fP
An alternative is to include this
data
within the shell script as a \fIhere\fP document, as in,
.DS
	for i
	do grep $i \*(HE!
	\*(DO\*(ZZ
	\*(DOfred mh0123
	\*(DObert mh0789
	\*(DO\*(ZZ
	!
	done
.DE
In this example
the shell takes the lines between \fB\*(HE!\fR and \fB!\fR
as the standard input for \fIgrep.\fP
The string \fB!\fR is arbitrary, the document
being terminated by a line that consists
of the string following \*(HE\|.
.LP
Parameters are substituted in the document
before it is made available to \fIgrep\fP
as illustrated by the following script
called \fIedg\|.\fP
.DS
	ed $3 \*(HE%
	g/$1/s//$2/g
	w
	%
.DE
The call
.DS
	edg string1 string2 file
.DE
is then equivalent to the command
.DS
	ed file \*(HE%
	g/string1/s//string2/g
	w
	%
.DE
and changes all occurrences of \fIstring1\fP
in \fIfile\fP to \fIstring2\|.\fP
Substitution can be prevented using \\
to quote the special character \fB$\fP
as in
.DS
	ed $3 \*(HE+
	1,\\$s/$1/$2/g
	w
	+
.DE
(This version of \fIedg\fP is equivalent to
the first except that \fIed\fP will print
a \fB?\fR if there are no occurrences of
the string \fB$1\|.\fP)
Substitution within a \fIhere\fP document
may be prevented entirely by quoting
the terminating string,
for example,
.DS
	grep $i \*(HE'end'
	\*(ZZ
	end
.DE
The document is presented
without modification to \fIgrep.\fP
If parameter substitution is not required
in a \fIhere\fP document this latter form
is more efficient.
.SH
2.4\ Shell\ variables\(dg
.LP
.FS
Also known as \fIenvironment variables\fB, see \fIenvironment\fB(7).
.FE
The shell
provides string-valued variables.
Variable names begin with a letter
and consist of letters, digits and
underscores.
Variables may be given values by writing, for example,
.DS
	user=fred\ box=m000\ acct=mh0000
.DE
which assigns values to the variables
\fBuser, box\fP and \fBacct.\fP
A variable may be set to the null string
by saying, for example,
.DS
	null=
.DE
The value of a variable is substituted
by preceding its name with \fB$\|\fP;
for example,
.DS
	echo $user
.DE
will echo \fIfred.\fP
.LP
Variables may be used interactively
to provide abbreviations for frequently
used strings.
For example,
.DS
	b=/usr/fred/bin
	mv pgm $b
.DE
will move the file \fIpgm\fP
from the current directory to the directory \fB/usr/fred/bin\|.\fR
A more general notation is available for parameter
(or variable)
substitution, as in,
.DS
	echo ${user}
.DE
which is equivalent to
.DS
	echo $user
.DE
and is used when the parameter name is
followed by a letter or digit.
For example,
.DS
	tmp=/tmp/ps
	ps a >${tmp}a
.DE
will direct the output of \fIps\fR
to the file \fB/tmp/psa,\fR
whereas,
.DS
	ps a >$tmpa
.DE
would cause the value of the variable \fBtmpa\fP
to be substituted.
.LP
Except for \fB$?\fP the following
are set initially by the shell.
\fB$?\fP is set after executing each command.
.RS
.IP \fB$?\fP 8
The exit status (return code)
of the last command executed
as a decimal string.
Most commands return a zero exit status
if they complete successfully,
otherwise a non-zero exit status is returned.
Testing the value of return codes is dealt with
later under \fBif\fP and \fBwhile\fP commands.
.IP \fB$#\fP 8
The number of positional parameters
(in decimal).
Used, for example, in the \fIappend\fP command
to check the number of parameters.
.IP \fB$$\fP 8
The process number of this shell (in decimal).
Since process numbers are unique among
all existing processes, this string is
frequently used to generate
unique
temporary file names.
For example,
.DS
	ps a >/tmp/ps$$
	\*(ZZ
	rm /tmp/ps$$
.DE
.IP \fB$\|!\fP 8
The process number of the last process
run in the background (in decimal).
.IP \fB$\(mi\fP 8
The current shell flags, such as
\fB\(mix\fR and \fB\(miv\|.\fR
.RE
.LP
Some variables have a special meaning to the
shell and should be avoided for general
use.
.RS
.IP \fB$\s-1MAIL\s0\fP 8
When used interactively
the shell looks at the file
specified by this variable
before it issues a prompt.
If the specified file has been modified
since it
was last looked at the shell
prints the message
\fIyou have mail\fP before prompting
for the next command.
This variable is typically set
in the file \fB.profile,\fP
in the user's login directory.
For example,
.DS
	\s-1MAIL\s0=/usr/spool/mail/fred
.DE
.IP \fB$\s-1HOME\s0\fP 8
The default argument
for the \fIcd\fP command.
The current directory is used to resolve
file name references that do not begin with
a \fB/\|,\fR
and is changed using the \fIcd\fP command.
For example,
.DS
	cd /usr/fred/bin
.DE
makes the current directory \fB/usr/fred/bin\|.\fR
.DS
	cat wn
.DE
will print on the terminal the file \fIwn\fP
in this directory.
The command
\fIcd\fP with no argument
is equivalent to
.DS
	cd $\s-1HOME\s0
.DE
This variable is also typically set in the
the user's login profile.
.IP \fB$\s-1PWD\s0\fP 8
The current working directory. Set by the \fIcd\fB command.
.IP \fB$\s-1PATH\s0\fP 8
A list of directories that contain commands (the \fIsearch path\fR\|).
Each time a command is executed by the shell
a list of directories is searched
for an executable file.
.ne 5
If \fB$\s-1PATH\s0\fP is not set
then the current directory,
\fB/bin\fP, and \fB/usr/bin\fP are searched by default.
.ne 5
Otherwise \fB$\s-1PATH\s0\fP consists of directory
names separated by \fB:\|.\fP
For example,
.DS
	\s-1PATH\s0=\fB:\fP/usr/fred/bin\fB:\fP/bin\fB:\fP/usr/bin
.DE
specifies that the current directory
(the null string before the first \fB:\fP\|),
\fB/usr/fred/bin, /bin \fRand\fP /usr/bin\fR
are to be searched in that order.
In this way individual users
can have their own `private' commands
that are accessible independently
of the current directory.
If the command name contains a \fB/\fR then this directory search
is not used; a single attempt
is made to execute the command.
.IP \fB$\s-1PS1\s0\fP 8
The primary shell prompt string, by default, `\fB$\ \fR'.
.IP \fB$\s-1PS2\s0\fP 8
The shell prompt when further input is needed,
by default, `\fB>\ \fR'.
.IP \fB$\s-1IFS\s0\fP 8
The set of characters used by \fIblank
interpretation\fR (see section 3.5).
.IP \fB$\s-1ENV\s0\fP 8
The shell reads and executes the commands in the file
specified by this variable when it is initially started.
Unlike the \fB.profile\fP file, these commands are executed by all
shells, not just the one started at login.
(Most versions of the shell specify a filename that is used if
\s-1ENV\s0 is not explicitly set. See the manual page for your shell.)
.RE
.SH
2.5\ The\ test\ command
.LP
The \fItest\fP command, although not part of the shell,
is intended for use by shell programs.
For example,
.DS
	test \(mif file
.DE
returns zero exit status if \fIfile\fP
exists and non-zero exit status otherwise.
In general \fItest\fP evaluates a predicate
and returns the result as its exit status.
Some of the more frequently used \fItest\fP
arguments are given here, see \fItest\fP(1)
for a complete specification.
.DS
	test s		true if the argument \fIs\fP is not the null string
	test \(mif file	true if \fIfile\fP exists
	test \(mir file	true if \fIfile\fP is readable
	test \(miw file	true if \fIfile\fP is writable
	test \(mid file	true if \fIfile\fP is a directory
.DE
The \fItest\fP command is known as `\fB[\fP' and may be invoked as
such.
For aesthetic reasons, the command ignores a close bracket `\fB]\fP' given
at the end of a command so
.DS
	[ -f filename ]
.DE
and
.DS
	test -f filename
.DE
are completely equivalent.
Typically, the bracket notation is used when \fItest\fP is invoked inside
shell control constructs.
.SH
2.6\ Control\ flow\ -\ while
.LP
The actions of
the \fBfor\fP loop and the \fBcase\fP
branch are determined by data available to the shell.
A \fBwhile\fP or \fBuntil\fP loop
and an \fBif then else\fP branch
are also provided whose
actions are determined by the exit status
returned by commands.
A \fBwhile\fP loop has the general form
.DS
	\fBwhile\fP \fIcommand-list\*1\fP
	\fBdo\fP \fIcommand-list\*2\fP
	\fBdone\fP
.DE
.LP
The value tested by the \fBwhile\fP command
is the exit status of the last simple command
following \fBwhile.\fP
Each time round the loop
\fIcommand-list\*1\fP is executed;
if a zero exit status is returned then
\fIcommand-list\*2\fP
is executed;
otherwise, the loop terminates.
For example,
.DS
	while [ $1 ]
	do \*(ZZ
	\*(DOshift
	done
.DE
is equivalent to
.DS
	for i
	do \*(ZZ
	done
.DE
\fIshift\fP is a shell command that
renames the positional parameters
\fB$2, $3, \*(ZZ\fR as \fB$1, $2, \*(ZZ\fR
and loses \fB$1\|.\fP
.LP
Another kind of use for the \fBwhile/until\fP
loop is to wait until some
external event occurs and then run
some commands.
In an \fBuntil\fP loop
the termination condition is reversed.
For example,
.DS
	until [ \(mif file ]
	do sleep 300; done
	\fIcommands\fP
.DE
will loop until \fIfile\fP exists.
Each time round the loop it waits for
5 minutes before trying again.
(Presumably another process
will eventually create the file.)
.LP
The most recent enclosing loop may be exited with the \fBbreak\fP
command, or the rest of the body skipped and the next iteration begun
with the \fBcontinue\fP command.
.LP
The commands \fItrue\fP(1) and \fIfalse\fP(1) return 0 and non-zero
exit statuses respectively. They are sometimes of use in control flow,
e.g.:
.DS
	while true
	do date; sleep 5
	done
.DE
is an infinite loop that prints the date and time every five seconds.
.SH
2.7\ Control\ flow\ -\ if
.LP
Also available is a
general conditional branch
of the form,
.DS
	\fBif\fP \fIcommand-list
	\fBthen	\fIcommand-list
	\fBelse	\fIcommand-list
	\fBfi\fR
.DE
that tests the value returned by the last simple command
following \fBif.\fP
.LP
The \fBif\fP command may be used
in conjunction with the \fItest\fP command
to test for the existence of a file as in
.DS
	if [ \(mif file ]
	then	\fIprocess file\fP
	else	\fIdo something else\fP
	fi
.DE
.LP
An example of the use of \fBif, case\fP
and \fBfor\fP constructions is given in
section 2.10\|.
.LP
A multiple test \fBif\fP command
of the form
.DS
	if \*(ZZ
	then	\*(ZZ
	else	if \*(ZZ
		then	\*(ZZ
		else	if \*(ZZ
			\*(ZZ
			fi
		fi
	fi
.DE
may be written using an extension of the \fBif\fP
notation as,
.DS
	if \*(ZZ
	then	\*(ZZ
	elif	\*(ZZ
	then	\*(ZZ
	elif	\*(ZZ
	\*(ZZ
	fi
.DE
.LP
The following example is an implementation of the \fItouch\fP command
which changes the `last modified' time for a list
of files.
The command may be used in conjunction
with \fImake\fP(1) to force recompilation of a list
of files.
.DS
	#!/bin/sh

	flag=
	for i
	do case $i in
	\*(DC\(mic)	flag=N ;;
	\*(DC\*(ST)	if [ \(mif $i ]
	\*(DC	then	cp $i junk$$; mv junk$$ $i
	\*(DC	elif [ $flag ]
	\*(DC	then	echo file \\'$i\\' does not exist
	\*(DC	else	>$i
	\*(DC	fi
	\*(DO esac
	done
.DE
The \fB\(mic\fP flag is used in this command to
force subsequent files to be created if they do not already exist.
Otherwise, if the file does not exist, an error message is printed.
The shell variable \fIflag\fP
is set to some non-null string if the \fB\(mic\fP
argument is encountered.
The commands
.DS
	cp \*(ZZ; mv \*(ZZ
.DE
copy the file and then overwrite it with the copy,
thus causing the last modified date to be updated.
.LP
The sequence
.DS
	if command1
	then	command2
	fi
.DE
may be written
.DS
	command1 && command2
.DE
Conversely,
.DS
	command1 \*(VT\*(VT command2
.DE
executes \fIcommand2\fP only if \fIcommand1\fP
fails.
In each case the value returned
is that of the last simple command executed.
.LP
Placing a `\fB!\fP' in front of a pipeline inverts its exit
status, almost in the manner of the C operator of the same name.
Thus:
.DS
	if ! [ -d $1 ]
	then
		echo $1 is not a directory
	fi
.DE
will print a message only if $1 is not a directory.
.SH
2.8\ Command\ grouping
.LP
Commands may be grouped in two ways,
.DS
	\fB{\fI command-list\fB ; }\fR
.DE
and
.DS
	\fB(\fI command-list\fB )\fR
.DE
.LP
In the first \fIcommand-list\fP is simply executed.
The second form executes \fIcommand-list\fP
as a separate process.
For example,
.DS
	(cd x; rm junk )
.DE
executes \fIrm junk\fP in the directory
\fBx\fP without changing the current
directory of the invoking shell.
.LP
The commands
.DS
	cd x; rm junk
.DE
have the same effect but leave the invoking
shell in the directory \fBx.\fP
.SH
2.9\ Shell\ Functions
.LP
A function may be defined by the syntax
.DS
	\fIfuncname\fP() \fB{\fI command-list\fB ; }\fR
.DE
Functions are invoked within a script as though they were separate
commands of the same name.
While they are executed, the
positional parameters \fB$1, $2, \*(ZZ\fR are temporarily set to the
arguments passed to the function. For example:
.DS
	count() {
		echo $2 : $#
	}

	count a b c
.DE
would print `b : 3'.
.SH
2.10\ Debugging\ shell\ scripts
.LP
The shell provides two tracing mechanisms
to help when debugging shell scripts.
The first is invoked within the script
as
.DS
	set \(miv
.DE
(\fBv\fP for verbose) and causes lines of the
script to be printed as they are read.
It is useful to help isolate syntax errors.
It may be invoked without modifying the script
by saying
.DS
	sh \(miv \fIscript\fP \*(ZZ
.DE
where \fIscript\fP is the name of the shell script.
This flag may be used in conjunction
with the \fB\(min\fP flag which prevents
execution of subsequent commands.
(Note that saying \fIset \(min\fP at a terminal
will render the terminal useless
until an end-of-file is typed.)
.LP
The command
.DS
	set \(mix
.DE
will produce an execution
trace.
Following parameter substitution
each command is printed as it is executed.
(Try these at the terminal to see
what effect they have.)
Both flags may be turned off by saying
.DS
	set \(mi
.DE
and the current setting of the shell flags is available as \fB$\(mi\|\fR.
.SH
2.11\ The\ man\ command
.LP
The following is a simple implementation of the \fIman\fP command,
which is used to display sections of the UNIX manual on your terminal.
It is called, for example, as
.DS
		man sh
		man \(mit ed
		man 2 fork
.DE
In the first the manual section for \fIsh\fP
is displayed..
Since no section is specified, section 1 is used.
The second example will typeset (\fB\(mit\fP option)
the manual section for \fIed.\fP
The last prints the \fIfork\fP manual page
from section 2, which covers system calls.
.sp 2
.DS
	#!/bin/sh

	cd /usr/share/man

	# "#" is the comment character
	# default is nroff ($N), section 1 ($s)
	N=n\ s=1

	for i
	do case $i in
.sp .5
	\*(DC[1\(mi9]\*(ST)	s=$i ;;
.sp .5
	\*(DC\(mit)	N=t ;;
.sp .5
	\*(DC\(min)	N=n ;;
.sp .5
	\*(DC\(mi\*(ST)	echo unknown flag \\'$i\\' ;;
.sp .5
	\*(DC\*(ST)	if [ \(mif man$s/$i.$s ]
	\*(DC	then
	\*(DC		${N}roff \(miman man$s/$i.$s
	\*(DC	else	# look through all manual sections
	\*(DC		found=no
	\*(DC		for j in 1 2 3 4 5 6 7 8 9
	\*(DC		do
	\*(DC		\*(DOif [ \(mif man$j/$i.$j ]
	\*(DC		\*(DOthen
	\*(DC		\*(DO\*(THman $j $i
	\*(DC		\*(DO\*(THfound=yes
	\*(DC		\*(DO\*(THbreak
	\*(DC		\*(DOfi
	\*(DC		done
	\*(DC		case $found in
	\*(DC		\*(Cano) echo \\'$i: manual page not found\\'
	\*(DC		esac
	\*(DC	fi
	\*(DOesac
	done
.DE
.ce
.ft B
Figure 1. A version of the man command
.ft R