Unix Unit 3 Part 1
Unix Unit 3 Part 1
Programming
The activities of the shell are not restricted to command interpretation alone. The shell has a11lidc
set of internal commands that can be strung together as a language. We need two chapters to diSCUli
this language. In this chapter, we focus on the Bourne shell-the lowest common denominator or,
shells. \\e have reserved the discussion of the advanced features of the Korn and Bash shells for l1ll!
II of the book. However, everything discussed here applies to both these shells too. The C sheU lllCI
totally different programming constructs and has been separately treated in Appendix A.
A shell program runs in interpretive mode. It is not compiled into a separate executable file asaC
program is. Each statement is loaded into memory when it is to be executed. Shell scripts consequcn~
run slower than those written in high-level languages. However, what makes shell programs powCJM
is that external UNIX commands blend easily with the shell's internal constructs. Speed is nix a
factor in many jobs we do, and in many cases, using the shell is an advantage-especially in sy~ci
administrative tasks. The UNIX system administrator must he_ an accomplished shell programmer.
WHAT You WILL LEARN
• How shell scripts are executed, and the role of the interpreter line.
• Make shell scripts interactive using read.
• Use positional parameters to read command line arguments.
• The role of the exit statement in terminating a script.
• Elementary decision making with the 11 and && operators.
• Comprehensive decision making with the if conditional.
• Use t fin tandem with test to perform numeric and string comparison, and test a file's attributes.
• Use the wild-card pattern matching features of case for decision making.'
• Integer computing and string handling using expr.
• Use a while loop to repeatedly execute a set of commands.
1
• Use a for loop with a list.
• Manipulate the positional parameters with the set and sh1 fJ stateme~ts.
• Use trap to control the behavior of a shell script when it receives signals,
Essential Shell Programming ~7 i}
TOPIC S OF SPECI AL INTER EST
ely.
• Redirect some statem ents to /dev /tty so the other statements can be manipulated collectiv
ing
• Exploit the hard linking feature with $0 to make a script behave in different ways depend
. on the name by which it is invoked.
• Use a here docum ent as the fourth source of standard input to run any interactive shell script
noninteractively.
echo
Use your vi editor to create the shell script, script.s h,{Fig. 14.l). The script runs three
the
commands and shows the use of variable evaluation and command substitution. It also prints
calendar of the curren t month.
#!/bin /sh
# script .sh: Sample shell script
echo •Today's date: 'date·•
echo •This month's calend ar:•
cal 'date •+%m 20%y•· # This month's calendar
echo "My shell: $SHELL·
Fig. H.l script. sh
ers
Note the co~me nt charact er(#) that can be placed anywhere i~ a line; the shell ignores all charact
a#. This is
placed on its right. Howev er, this doesn't apply to the first line which also begins with
d by the
the interpreter line that was mentio ned previously. It always begins with # ! and is followe
shell.
pathnam e of the shell to be used for runnin g the script. Here, this line specifics the Bourne
To run the script, make it cxecut.abl~ first a~d then invoke the script name:
\
UNIX: Concepts and Applications
$ chmod +x script.sh
$ script.sh PATH must include the•
Today's date: Mon Jan 6 10:02:42 IST 2003 or else use ./script.sh
This month's calendar:
January 2003
Su Mo Tu We Th Fr Sa
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
My shell: /bin/sh
The script is too simple to need any explanation-no inputs, no command line arguments and00
control structures. We'll be progressively adding these features to our future scripts.
We mentioned that shell scripts arc executed by-a child shell. Actually, the child shell read'. aM
executes each statement in sequence (in interpretive mode). You can also explicitly spawn achild ii
your choice with the script name as argument:
sh script.sh Will spawn a Bourne shell
·•,,
When used in this way, the Bourne sub-shell opens the file but ignores the interpreter line. The
script doesn't need to have execute permission either. We'll make it a practice to use the interpreter
line in all our scripts. •
lip: The pathname of the shell specified in the interpreter line may not match the actual pathn~ 00
your system. This sometimes happens with downloaded scripts. To prevent these sc~pts from bre~
make a symbolic link between the two locations. Note that root access is required to make a ~
link between /bin/ksh and /usr/bin/ksh.
$ empl.sh
Enter the pattern to be searched : director
Enter the file to be used: emp.lst
Searching for directo r from file emp.lst
9876lja1 sharma !directo r lproductionll2/03/50l7000
2365lbarun sengupt a !directo r !personnel lll/05/4717800
Selected records shown above
The script first asks for a pattern to be entered. Input the string di rector, which is assigned to the
variable pname. Next, the script asks for the filename; enter the string emp. l st, which is assigned to
the variable fl name. grep then runs with these two variables as arguments. I
A single read stateme nt can be used with one or more variables to let you enter multiple arguments:
read pname flname
If the number of argume nts supplied is less than the number of variables accepting them, any
leftover variables will simply remain unassigned. However, when the number ofarguments exceeds
the number of variables, the remainin g words arc assigned to the last variable.
#!/bin/sh .
# emp2.sh: Non-interactive version - uses conmand hne arguments
I
echo •Program: $0 . I $0 contains the program name
The number of arguments specifieJ is$#
The arguments are $•• # All arguments stored in $*
grep "$1° $2
echo "\nJob Over"
$0- Holds the command name itself. You can link a shell script to be invoked by more than0rt
name. The script logic can check $0 to behave differently depending on the name bywhl~
it is invoked.
The next script, emp2.sh (Fig. 14.3), runs grep with two positional parameters that are setbytft
script arguments, di rector and emp .1st: '
$ emp2.sh director emp.lst
,, Program: emp2.sh
The number of arguments specified is 2
The arguments are director emp.lst
1006 I chancha l si nghvi Idi rector Isales I 03/09/38, 6700
652lllalit chow~ury !director !marketing 126/09/45 8200
Job Over
When arguments are specified in this way, the first word (the command itself) is assigned tol0,tft
second word (the first argument) to ·u, and the third word ( the second argument) to 12. y01
can use more positional parameters in this way up to $9 (and usi~g the shift statement,i·ou
can go beyond). .
When you use a multiword string to represent a single command line argument, you mustquru
it To look for chanchal singhvi, use emp2.sh •chanchal stnghvt • emp.1 st. You have notedtrus
quoting requirement when using grep also (13.1).
All assignments to positional and special parameters are made by the shell. You can't reallytamf<!
with their values, except in an indirect fashion, but you can use them to great advantage in scvml
ways. They will be used over and over again in shell scripts, and arc listed in Table 14.1.
Bash Shell: $0 in Bash prepends the . / prefix to the script name. In the example above, it would 1w
shown . / emp2. sh instead of emp2. sh. You need to keep this in mind when you make use of 10
develop portable scripts.
Essential Shell Programming
lip: To find out whether a command executed successfully or not. simply use echo $? after the~·
0 indicates success, other values point to failure. •
Note: Success or failure isn't as intuitive as it may seem. The designer of grep interpre~
inability to locate a pattern as failure. The designer of sed thought otherwise. The co~
sed -n l /manager/p' emp.1 st returns a true value even if manager is not found!
exists in
exit status of grep. This time we'll search /etc/pa sswd for the existence of two users; one
the file and the other doesn't :
$ emp3.sh ftp
ftp:*:3 25:15:F TP User:/u sersl/h ome/ft p:/bin/ true
Pattern found - Job Over
$ eap3.sh mail
Pattern not found
'
#I/bin /sh
I emp3.s h: Using·if and else
#
if grep /etc/~a sswd 2>/dev /null # Search username at begjnning of line
then. . I
• echo "P•tte rn found - Job Over•
else
echo •Patte rn not found"
fi l •·
• I
I,
Fig. 14.4 emp3.sh
UNIX: Concepts and Applications
We'll discuss the third form of the if statement when we discuss test. The condition placedindit
command line ofthe if statement will henceforth be referred to as the control command. Youci
0
use if in this way with any executabl~ program. Amazing power indeed!
The operators ·are quite mnemonic; -eq implies equal to, -gt implies greater than, and so on.
N~meric comparison in the shell is confined to integer values only; decimal values arc simplJ
truncated. To illustrate how numeric tests are performed, we'll assign some values to three variables
and numerically compare them: ,
S xsS; y=7; z=7.2
$ test $x -eq $y; echo $7
1 Not equal
$ test $x -lt $y; echo $7
0 True
$ test $z -gt $y; echo $7
1 7.2 is not greater than 71
$ test $z -eq $y; echo $7
0 . 'j . 7.2 is equal to 71
The last two tests prove conclusively that numeric compariso~ is restricted to integers only, Having
used test as a standalone feature, you can now use it as 1f's ~ontrol command. The next scripi,
emp3a.sh (Fig. 14.5) uses test in an 1f-e11f-else-fi construct'(Form 3) to evaluate the shell
parameter,$#. It displays the usage when no arguments are input, runs grep if two arguments art
entered and displays an error message otherwise. :,
Essential Shell Programming
279 1
. #!/bin/sh
# emp3a.sh: Using test, $0 and$# in an if-elif-if construct
*if test$# -eq O; then
echo "Usage: $0 pattern file" >/dev/tty
elif test$# -eq 2; then
grep "$1" $2 11 echo "$1 not found in $2• >/dev/tty
. else •
echo "You didn't enter two arguments" >/dev/tty
fi
' Fig. 14.5 emp3a. sh
Why did we redirect the echo output to /dev/tty? Simple, we want the script to work both with
and without redirection. In either case, the output of the echo statements must appear only on the
terminal. These statements arc used here as "error" messages even though they are not directed to
.the standard error. Now run the script four times and redirect the output every time:
>
$. emp3a.sh foo
Usage: emp3a.sh pattern file
$ eap3a.sh ftp> foo
You didn't enter two argum~nts
$ eap3a.sh henry /etc/passwd > foo
henry not found in /etc/passwd
$ •p3a.sh ~tp /etc/passwd > foo
$ cat foo
ftp:*:32?:15:FTP User:/usersl/home/ftp :/bin/true
The importance of /dev /tty as a mechanism of explicitly redirecting an output stream shows up in
this example. You must appreciate this and use this feature in shell scripts when you like to have
the redirection option open. The above script works just as well even if you don't redirect it.
llp: An application may need to be designed in a flexible manner to allow redirection of an entire script
or its participation in a pipeline. In that case, you need to ensure that messages meant to draw the
attention of the user (mainly from echo) are redirected to >/dev/tty.
Shorthand for test test is so widely used that forrunat ely there exists a shorthand mcth~i
', executing it. A pair ofrectangular brackets enclosing the expressi on ca~ replace it Thus, the foUowuig
two forms arc equivalent:
test $x ·-eq $y
[ $x -eq $y ]
Note that you must provide whitespace around the operator s (like -eq), their operands (like Ix)
and inside the [and]. The second form is easier to handle and will be used henceforth. But dont
forget to be liberal in the use ofwhitespace here!
Note: It is a feature of most programming language s that you c.an use a conditio n like if (x), wl'tt!I
is a variable or expression. If x is greater than O, the statemen t is said to be true. We can also appfyllt
same logic here and use if [ $x ] as a shorthand form of i f [ $x -gt O 1.
··11/bfn/sh
# emp4.sh: Checks user input for null values - Finally runs emp3a.sh
developed previously
'1f [ $# -eq O ] ; then •
echo ·•Enter the string to be searched: \c• -
read pname
ff [ -z •$pname" ] ; then # -z checks for a null string
echo •vou have not entered the string•; exit 1
ff
echo "Enter the filename_to be used: \c"
read flname
ff [ I -n •$flname" ] ; then I I -n is the same as -z
echo •vou have not entered the filename" ; exit 2
ff
emp3a.sh •$pname" •$flname" # Runs the script that will do the job
else
emp3a.sh $* # We'll change$* to"$@" soon
fi
Fig. 14.6 I emp4. sh
11p: It's safer to use •$t• instead of$*. When you employ multiword strings as arguments to a shell
script. it's only •st• that interprets each quoted argument as a separate argument. As the output above
suggests, If you use $*, the shell makes a wrong count of the arguments.
test also permits the checking of more than one condition in the same line, using the -a (AND)
and -o (OR) operators. You can now simplify the earlier script to illustrate this fc;ature. Accept both
inputs in succession and then make the check with a compound ff state.q}ent: .
. %
H [ -n • $pname• -a -n • $fl name• ] ; then
• emp3a. sh •$pname• •$fl name•
else·
echo •At least one input was a null string" exit 1
f1
I~ UNIX: Concepts and Applications
The test output is true only if both variables arc nonnull strings, i.e., the user entcn •
nonwhitespace characters when the_ script pauses twice.
---------~:--t
lip: Observe that we have been quoting our variables wherever possible. Quoting is essential~,
assign multiple words to a variable. To tiy that out, drop the quotes to use the statement 1f I•1 11"'1,
When you input two words, or even a null string to be assigned to pname, you'll often encounte!illt
Q.ioting is safe with no adverse consequences. '
,,.
I
UNIX: Concepts and Applications
#!/bin/sh
I menu.sh: Uses case to offer 5-item menu
I
echo II MENU\ n
1. List of files\n2. Processes of user\n3. Today's Date.
4. Users of system\n5. Quit to UNIX\nEnter your option: \c"
read choice
case "$choice• ; n , -.
l)ls~l;;
2) ps -f ::
3) date ;;
4) who ;;
5) exit ; ;
*) echo •Invalid option• I ; ; not really required for the last opu111
esac
Fig.14.8 menu.sh
case first matches erpres.rion with pattern!. If the match succeeds, then it executes comm.,~
which may be one or more commands. If the match fails, then pattem2 is matched, and io '
Each command list is terminated with a pair of semicolons,and the entire construct is cloi(d
esac (reverse of case). '
Consider a simple script, ,menu. sh (Fig. 14.8) that uses case. The script accepts values from 1,
and performs some action depending on the number keyed in. The five menu choices aredii~
with a multi-line echo statement. ' • •· , '
case matches the value of $choice with the strings 1, 2, 3, 4 an1 5. If the user en~ers a l,thclH
command is executed. Option 5 quits the program. The last option (*) matches any o!)(ir4,
matched by by the previous options. We'll make goo~ use ~f the * later.
You can see today's date by choosing the third option:
$ aenu.sh
MENU 'l
1. List of files
2. Processes of user
3. Today's Date
4. Users of system
5. Quit to UNIX • ,I
Enter your option: 3
Tue Jan 7 18:03:06 1ST 2003
case can't handle relational and file tests, but it matches strings with compact code. ltisilio~
effective when the string is fetched by command substitution. Ifyou cut out the first fiddfn:tl
date output, you can use this ~ase construct to do different things, depending on the dayofmctd
case ·date I cut -d" • -fl• in Outputsthree-characterdaystring
2851
Essential Shell Programming 1;.....a·
case can also hand le num bers, but only by treating them
as strings. Some ofour previous programs
used tf and test to chec k the value of$# . You can use
case to match$# directly (without using
it to make numeric checks (of the type
test) with specific values (0, I, 2, etc.), but you can't use -
$1 -gt 2).
#I/bin/sh
# come.sh: Script that is called by different names I
#
lastfile='ls -t *.c I head -n 1·
comnand=$0 # Assigning a special parameter to a variable - OK
executable=·expr $lastfile : '\(.*\).c•· # Removes .c; foo.e becomes foo
case $conmand in
*rune) $executable;; # Runs the executable
*vie) vi $lastfile ;;
*come) cc -o $executable $lastfile &&
echo "$lastfile compiled successfully 0 ;;
esac
Fig.14.9 COIie.sh
Essential Shell Programming 289 I
14.11 while: LOOPING
None of the pattern scanning scripts developed so far offers the user another chance to rectify a
faulty response. Loops let you perform a set of instructions repeatedly. The shell features three
types of loops--while, until and for. All of them repeat the instruction set enclosed by certain
keywords as often as their control command permits.
The wh f 1e statement should be quite familiar to most programmers. It repeatedly performs a set of
instructions until the control command returns a true exit status. The general syntax of this
command is as follows:
wh1 le condition ,is true
do Note the do keyword
comma.nds
done Note the done keyword
The commands enclosed by do and done are executed repeatedly as long as condition remains true.
You can use any UNIX command or test as the condition, as before.
We'll start with an orthodox whf le loop application. The script, empS.sh Fig. 14.10), accepts a code
and description in the same line, and then writes the line to newl i st. It then prompts you for more
entries. The loop iteration is controlled by the value of $answer.
We have redirected the output of two echo statements to /dev /tty for reasons that will be apparent
later. We'll make a small but significant modification later, but let's run it first:
$ empS.sh
Enter the code and description: 03 analgesics
Enter any more (y/n)? y
#I/bin/sh
I emp5.sh: Shows use of the while loop
I
answer•y I Must set it toy first to enter the loop
while [ •sanswer• = •y•] I The control COITllland
do
echo •Enter the code and description: \c" >/dev/tty
read code description # Read both together
echo •Scodel$description• >> newlist # Append a line to newlist
echo •Enter any more (y/n)? \c" >/dev/tty
read anymore
case $anymore in
y*IY*) answer=y ;; I Also.accepts yes, YES etc.
n*IN*) answer=n •• # Also accepts no, NO etc.
*) answer=y ; ; # Any other reply means y
esac
done
Fig.14.10 empS.sh
UNIX: Concepts and Applications
Note: Redirection is also available at the f1 and esac keywords, and includes input redirectiona,1:1
piping:
..
done< param.lst Statements in loop take input from param. lst
done I while true' Pipes output to a whi 1e loop
fi > foo Affects statements between i f and f i
esac > foo Affects statements between case and esac
#I/bin/sh
# monitfile.sh: Waits for a file to be created
I
while [ ! -r invoice.1st] # While the file invoice.1st can't be read
do
sleep 60 # sleep for 60 seconds
done
all oc .pl # Execute this program after exiting loop
Fig.14.11 monitfile.sh
I ;
Note: The shell also offers an unti 1 statement which operates with a reverse logic used in whi 1e. With
unti 1, the loop body executed as long as the condition remains false. Some people would have
preferred to have written a previous while control command as until [ -r invoice. lst ] . This form
is easily intelligible.
This method is most suitable when the list is large and you don't conside r it practicable
to specify
its contents individually. It's also a clean arrange ment because you can change the list
without
having to change the scripL
List from Wild-cards When the list consists of wild-cards, the shell interpre ts them as filenames.
for is thus indispensable for making substitu tions in a set of files with secl. Take, for instance, th~
loop which works on every HTML file in the current director y:
for file in *,htm *.html ; do
sed 's/strong/STR0NG/g
s/img src/IMG SRC/g' $file> $$
mv $$ $file
gzip $file
done
I.
Essential Shell Programming ~91I
#I/bin/sh
# emp6.sh -- Using a for loop with positional parameters
I
for pattern in $@ 11
do11
·; # Decided not to use$* - Section 14.7.2
grep "$pattern" emp.lst I I echo "Pattern $pattern not found"
done
Fig. 14.12 emp6.sh
In this loop, e1lch HTML filename is assigned to the variable file in turn. sed performs some
substitution on each file and writes the output to a temporary file. This filename is numeric-
expanded from the variable$$ (the PID Jfthe current shell). The temporary file is written back to
the original file with mv, and the file is finally compressed "".ith gzip.
Listfrom Positional Parameters for is also used to process positional parameters that are assigned
from command line arguments. The next script, einp6.sh (Fig. 14.12), scans a file repeatedly for
each argument. It uses the shell parameter "$@" (and not $*) to represent all command line
arguments.
Execute the script by passing four arguments, one of which is a multiword string:
$ emp6.sh 2345 1265 •ja1 sharma• 4379
2345jj.b. saxena ' jg.m. !marketing j12/03/45j8000
126Sjs.n. dasgupta !manager Isales 112/09/6315600
9876jjai shanna !director jproductionjl2/03/50j7000
Pattern 4379 not found
Since for is mostly used with •s~• to access command line arguments, a blank list defaults to this
parameter. Thus, these two statements mean the same thing:
for pattern in ·s@·
for pattern "$@" is implied
Note that the script won't work properly if we replaced "$@"with$*. Make this change and then
see for yourself how the script behaves.