0% found this document useful (0 votes)
3 views

Unix Unit 3 Part 1

Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views

Unix Unit 3 Part 1

Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 24

Essential Shell

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.

14.1 SHELL SCRIPTS


the
When a group of comma nds have to be executed regularly, they should be stored in a file, and
ly use
file itselfexecute d as a shell script or shell progra m. Though it's not mandatory, we normal
the .sh extensi on for shell scripts. This makes it easy to match them with wild-cards.
Shell scripts arc execute d in a separate child shell process, and this sub-shell need not be of the
Korn
same type as your login shell. In other words, even ifyour login shell is Bourne, you can use a
but you
sub-shell to run your script. By default , the child and parent shells belong to the same type,
for your script.
can provide a special interpreter line in the first line ofthe script to specify a different shell
There are two
Tip: Generally, Bourne shell scripts run without problem in the Korn and Bash shells.
by appropriate code
Issues in Bash, however. FH"St, Bash evaluates $0 differently. This has to be handled
and \n) unless the
In the script. Second ; it doesn't recognize escape sequences used by echo (like \c
-e option is·used. To make echo behave in the normal manner, place the statement
shopt -s xpg_echo
In your re file (probably, -/. bash re).

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.

14.2 read: MAKING SCRIPTS INTERACTIVE


The read statement is the shell's internal tool for taking input from the user, i.e., making scripts
interactive. It is used with one or more variables. Input supplied through the standard inputisrcao
into these variables. When you use a statement like
read name
the script pauses at that point to take input from the keyboard. Whatever you enter is stored in the
variable name. Since this is a form of assignment, no $ is used before name. The script, empl.sb
(Fig. 14.2), uses read to take a s.earch string and filename from the terminal. .
Shell scripts accept comments prefixed by I anywhere in a line. You know what the sequence \c
does (3.3). Run the script an.d specify the input when the script pauses twice:
< \ .
Essential Shell Programnying P.3 1
#I/bin/ sh
I empt.sh : Interac tive version - uses read to take two inputs
#
echo •Enter the pattern to be searched: \c" I No newline
read pname
echo "Enter the file to pe used: \c• I Use echo -e or
read flname I shopt -s xpg_echo in Bash
echo •search ing for $pname from file $flname•
grep •$pname" $flname
echo •selecte d records shown above"
Fig. 14.2 empl. sh

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

14.3 USING COMMAND LINE ARGUMENTS


Like UNIX comman ds (which arc written in C), shell scripts also accept arguments from the
command line. They can, therefore, run nonintcractively and be used with redirection aQd pipelines.
When argume nts arc specified with a shell script, they arc assigned to certain special "variab lcs"-
rathcr positio nal parameters. .
The first argume nt is read by the shell into the,parameter $1, the second argument into $2, and so
on. You can't technica lly call them shell variables because all variable names start with a letter. In
addition to these position al parameters, there are a few other special parameters used by the shell.
Their significance is noted below:

$* - It stores the complet e set of positional parameters as a single string.


•$1 - It is set to the number of arguments specified. Thi~ lets you design scripts that check
. whether the right number ' of arguments have been entered.
UNIX: Concepts and Applications

#!/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"

Fig. 143 emp2. sh

$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

Table 14.1 Special Parameters Used by the Shell


Shell Parameter Significance
$1, $2... Positional parameters representing command line arguments
$1 Number of arguments specified in command line
$0 ' Name of executed command
$* Complete set of positional parameters as a single string
ns(ilu Each quoted string treated :is a separate argument (recommended over$*)
$1 Exit status oflast command
$$ PID of the current shell (9.1.1)
$1 PID of the last background job (9.9.1)

14.4 exit AND EXIT STATUS OF COMMAND


C programs and shell scripts have a lot in common, and one of them is that they both use the same
command (or function in C) to terminate a program. It has the name exit in the shell and exit()
_ in C. We'll take up the exit function in Part II of this book, but in this section, we'll examine the
shell's exit command. The command is generally run with a numeric argument:
exit O Used when everything went fine
, exit 1 Used when something went wrong
These are two very common exit values. You don't need to place this statement at the end of every
shell script because the shell understands when script execution is complete. Rather, it's quite
often used with a command when it fails.
Once grep couldn't locate a pattern (13.1); we said then that the command failed. What we meant
was that the ext t function in the grep code was invoked with a nonzero argument (exit(l)). This
value (1) is communicated to the calling program, usually the shell. .
It's through the exit command or function that every command returns an exit status to the
caller. Further, a command is said to return a true exit status ifit executes successfully, and false if it
fails. The cat command as used below:
$ cat foo
cat: can't open foo
returns a nonzero exit status because it couldn't open the file. The shell offers a variable($?) and a
command (test) that evaluates a command's exit status.
Tke ltlrameter $1 The parameter $? stores the exit status of the last command. It has the value 0
if the command succeeds and a nonzero value if it fails. This parameter is set by exit's argument.
Ifno exit status is specified, then$? is set to zero (true). Try using grep in these ways and you'll see
it returning three different exit values:
$ grep director emp.lst >/dev/null; echo $7
0 Success
$ grep Mnager emp.lst >/dev/null; echo $1
1 Faifore-in finding pattern
UNIX: Concepts and Applications

$ grep manager emp3.1st >/dev/null; echo $7


grep: can't open emp3.lst Failure-in openingfile
2
The exit status is extremely important for programmers. They_ use it to devise program lo~c~
branches into different paths depending on the success or failure of a command. For cxalll~
there's no point in continuing with script execution if an important file doesn't exist or can't be~

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!

14.5 THE LOGICAL OPERATORS && AND I I-CONDITIONAL EXECUTION


The script emp_l. sh has no logic to prevent display of the message, Se1ected 1 i nes shown ahii!
,,,. when the pattern search fails. That is because we didn't use grep' s exit status to control the fiOV1 '.
. ...
I •h the_progra_m. The shell provides two operators that allow conditional execution-the &&and I~
., ,,. which typ1cally have this syntax:
I .. cmdl && cmd2
cmdl 11 cmd2
The && delimits two commands; the command cmd2 is executed only when cmdl succeeds. 'kt
can use it with grep in this way: . • ·
$ grep 'director' emp. lst && echo "pattern found tn ft1e•
1006lchanchal singhvi !director Isales 103/09/3816700
652lllalit chowdury !director !marketing 126/09/4518200
pattern found in file
The 11 operator plays an inverse role; the second command is executed only when the firstfaikI
you "grep" a pattern from a file without success, you can notify t~e failure:
$ grep 'manager' emp. lst 11 echo •Patt~rn not found•
Pattern not found
The 11 goes pretty well with the ext t command. You often need to terminate a script whti1i
command fails. The scriptemp2.sh can be modified to include this feature. The followingtwoliao
ensure that the program is aborted when the grep command fails and a message is printed ifitsucc®
grep •sin $2 11 exit 2 No point continuing ifsearch fails
echo •Pattern found - Job Over" Executed only ifgrepsucceeds
This segment makes rudimentary decisions which the previous scripts couldn't: In fact, the&&~
_// operators are recommended for making simple decisions. When co~ple~ decision making,
involved, they have to make way for the 1f s~telhe~~- • •
,. .
Essential Shell Program ming ~111

14.6 THE if CONDITIONAL


' on. In
The 1f statem ent makes two-way decisions depending on the fulfillment of a certain conditi
es:
the shell, the statem ent uses the following forms, much like the one used in other languag

i f comma nd is successful if command is successful if command is successful


then
I
then then
execute comma nds · execute commands execute commands
else fi e1i f command is successful
execute comma nds then ...
fi else ...
fi

Form! Form2 Form3


is
As in BASIC, if also require s a then. It evaluates the success or failure of the comma nd that
ng it is
specified in its "comm and line." If comma nd succeeds, the sequence of commands followi
stateme nt is not
executed. Ifcomma nd fails, then the else statement (if present) is executed. This
ter
always require d, as shown in Form 2. Every if is closed with a corresponding fi, and you'll encoun
an error if one is not present .
ines the
What makes shell program ming so powerful is that a command's exit status solely determ
nds
course ofaction pursue d by many ofthe shell's important constructs like i f and while. All comma
program ming
return an exit status as we saw with cat and grep, so you can imagine where shell
can lead us.
the
In the next script, emp3. sh (Fig. 14.4), grep is first executed and a simple if-else constru ct tests
0

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!

14.7 USING test AND [] TO EVALUATE EXPRESSIONS


When you use if to evaluate expressions, you need the test statement because the true orfalst
values returned by expressions can't be directly handled by if. test uses certain operators to cvaltUtt
the condition on its right and returns either a true or false exit status, which is then used by iffor
making decisions. test works in three ways:
• Compares two numbers.
• <l:ompares two strings or a single one for a n~ll value.·
• Checks a file's attributes. -
These tests can be ·made by test in association with the shell's other statements also, but for~c
present we'll stick with if. test doesn't display any output but simply sets the parameter$?. In~
following sections, we'll check this value. •

14.7.1 Numeric Comparison


•r The numerical comparison operators (Table 14.2) used by test have a form different from whit
you would have seen anywhere. They always begin with a - (hyphen), followed by a two-letter
string, and enclosed on either side by whitespace. Here's a typical operator:
-ne Not equal

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.

lable 14.2 Numerical Comparison Operators Used by test


Opentor Meaning
-eq Equal to
-ne ••• • Not equal to
-gt Greater than
-ge - Greater than or equal to
-lt Less than
1
-le Less than or equal to
l;j UNIX: Concepts and Applications

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.

14.7.2 String Comparison


test can be used to compare strings with yet another set of operator s (Table 14.3). Equality is
performed with= and inequality with the C-type operator l =. Other test checks can be negated~
the! too. Thus, [ ! -z $string ] negates [ -z $string ].
Our next script, eap4.sh (Fig. 14.6) behaves both interacti vely and noninteractively..Whenrun
without arguments, it turns interactive and takes two inputs from you. It then runs emp3a.s~ the
script developed previously, with the supplied inputs as argumen ts. Howeve r, when emp4.shi15C~
is run with at least one argument, it runs emp3a. sh with the same argumen ts. In either case, eiipll,i
is run, whi~h finally checks for the actual number of argumen ts entered before making a search
with grep.·
When the script runs in th~ interactive mode, the check for a null string is made with [ -z "$pname' I
as well as with [ ! -n •$fl name• ] , since they arc really two differen t ways of saying the same
thing. Note the use of$* in the noninteractive mode; this is the way you can use the same set of
arguments to run another script. Very soon, we'll find a good reason to change$ * to "$@". Let'slirit
run the script interactively:
$ eap4.sh
Enter the string to be searched : {Enter]
You have not entered the string
$ ellp4.sh
Enter the string to be searched : root
Enter the filename to be used: /etc/passwd
root:x:0 : 1:Super- User:/: /usr/bin /bash From emp3a .sh
Sec how two scripts cooperated in displaying root's entry from /etc/pas swd. When we run the
script ~ith arguments, ellll)4. sh bypasses all ofthe above actiyities and calls emp3a. sh to perform~
validation checks: •
$ eap4.sh ja1 . I. .
- You didn't enter two arguments
Essential Shell Programming

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

$ ellp4.sh Jat eap. 1st _


9876IJai shanna !director lproductionll2/03/S0l7000
.. $ ellp4.sh •J•t shar,•• e11p. 1st
You didn't enter two arguments
The last message could take you by surprise as jai sharma should have been treated as a single
argument by the script. Indeed it has been, but$* doesn't understand it as such; j ai and sharma are
embedded in $* as separate arguments. $# thus makes a wrong argument count. The solution to
• this is simple: Replace$* in the script with •$~• (with quotes) and then run the script again:
$ ellp4.sh •Jat sha.-..• ellJl.lst
9876l~ai shanna !director lproductionl12/03/S0l700~

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

lable 143 String Tests Used by test


Tt:st True if
sl =s2 =
String sl s2
sl I= s2 String sl is not equal to s2
-nstg String stg is not a null string
-zstg String stg is a null string
stg Stringstg is assigned and not null
sl ==s2 =
StringJJ s2 (Korn and Bash only)

" 14.7.3 File Tests


test can be used to test the various file attrib~tes like its type (file, directory or symboliclinijm
permissions (read, write, execute, SUID, etc.). Both perl and the UNIX system call libnq
offcrthese facilities (Table 14.4). Let's test some attributes of the file emp. 1st at the prompt
$ ls -1 emp.lst
-rw-rw-rw- 1 kumar group 870 Jun 8 15:52 emp.lst
$ [ -f emp. lst ] ; echo $? An ordinaryfi/1?
0 Yes
$ [ -x emp. lst ] ; echo $? An w:c111ablefik1
1 No
$ [ I -w emp.lst] II echo "False that file is not writable"
False that file is not writable
The I negates a test, so [ ! -w foo ] negates [ -w foo ] . Using these features, you can~
script, f11etest.sh (fig.14.7), that accepts a filename as argument and then performs anude
tests on it. Test the script with two filenames-one that doesn't exist and one that does:
$ ftletest.sh eap3.lst
File does not exist
$ f11etest.sh eap.lst •, I

File is both readable and writable


This completes the discussion on the three domains of test-numeric comparison, tcsril!
strings and file attributes. Even though we used test with the ff statement in all ofoureu~
test returns an exit status only, and can thus be used with any shell construct that US(lll
status. test also finds wide application in the whfle statement.
Essential Shell Programming ~83 I
#!/bin/sh
- I filetest.sh: Tests file attributes
I
if [ ! -e $1 ] ; then
echo "File does not exist"
e 1 if [ I -r $ 1 ] ; then
echo "File is not readable"
elif [ ! -w $1] ; then
echo "File is not writab 1e"
else
echo "File is both readable and writable"
fi

Fig. 14.7 filetest.sh

Table 14.4 Ftle-rdated Tests with test


Test TrueifFile
-f file file exists and is a regular file
-r file ' file exists and is readable
-w file file exists and is writable
-x file file exists and is executable
-d file file exists and is a directory
·•s file file exists and has a size greater than zero
-e file file exists (Korn and Bash only)
-ufile file exists and has SUID bit set
-kfile file exists and has sticky bit set
-lfile file exists and is a symbolic link (Korn and Bash only)
/1 -ntj2 fl is newer than fl (Korn and Bash only)
fl -otj2 fl is older than j2 (Korn and Bash only)
/1 -efj2 fl is linked to/2 (Korn and Bash only)

14.8 THE case CONDITIONAL


The case statement is the second conditional offered by the shell. It doesn't have a paraUel either in
C (switch is similar) or perl. The statement matches an expression for more than one alternative,
and uses a compact construct to permit multiway branching. case also handles string tests, but in
a more efficient manner than t f. The general syntax of the case statement is as follows:
case expression in
pallernl) commands] ; ;
pattern2) commands2 ; ;
pattern3) commands3 ; ;
esac

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

Mon) tar -cvf /dev/fdO $HOME••


Wed) scp -r $HOME mercury:/home/henry ;; ;;
Fri) find $HOME -new er. last_full_backup_time -pri nt> taril ist
esac
*) ...

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

14.8.1 Mat chin g Multiple. Patt erns


rn. Programmers frequently encounter
cue can also specify the same action for more than one patte
n and N). Like grep -E and egrep, case
a logic that has to test a user response for both y and Y (or
ssion YIY can be used to match Yin
uses the I to delim it mult iple patterns. For instance, the expre
both uppe r and lowercase:
echo "Do you wish to continue? (y/n}: \c"
read answer
case"$answer• in
y IY} , • Null statement, no action Jo be performed
nl N) exit ; ;
esac
mented with tf. case becomes an
The same logic wou ld requ ire a larger num ber oflines ifimple
autom atic choi ce whe n the num ber of matching options is high.

14.8.2 Wild-Cards: case Uses Them


cards. It uses the filename matching
case has a supe rb strin g matc hing feature that uses wild-
only to match strings and not files in the
metacharacters *, ? and the character class (8.3. 3)-b ut
cu~ t directory. The revised case construct ofa previous
example lets the user answer the question
in several ways:
case "$answer• in
[yY] [eE] *) ; ; Matches YES, yes, Yes, YEs, yES, etc.
[r,N] [oO]) exit • • Matches NO, no, nO and No
• *) echo "Invalid response"When e11erythingelsefails
esac
enough. Note that the * appears in two
Wi~d-card usage in the first two options appears simple
card. In the last option, it provides a
opbons. In the first optio n, it behaves like a normal wild-
case option doesn't need ; ; but you can
refuge for all othe r nonm atche d options. Note that the last
provide them if you want .

14.9 expr: COMPUTATION AND STRING HANDLING


than another, but it doesn't have any
The Bour ne shell can chec k whet her an integer is greater
. computing. features at all. It has to rely on the external
expr command for that purpose. This
comm and com bine s two functions in one: •
------- ---- ----·-

UNIX: Concepts and Applications

• Performs arithmetic operations on integers.


• Manipulates strings.
We'll use expr to perform both these functions, but with not-very-readable code when it comes~
string handling If you are using the Korn shell or Bash, you have better ways of handling ~'it
bJt
things (21. 7), you must also understand the helplessness of Bourne. It's quite possible thaty~
have to debug Jomeone else's script which contains expr. •
Ii . .
14.9.1 Computation -
expr can perform the four basic arithmetic operations as well a~ the modulus (remainder) functi00:
$ x=3 y=S Multiple assignments without a ;
$ expr 3 + 5
8
$ expr $x - $y
-2
$ expr 3 \ * 5 Asterisk has to be escaped
15
$ expr $y / $x
1 Decimal portion truncated
$ expr 13 %5
3
The operand, be it+, -, * etc., must be enclosed on either side by whitespace. Observe thattJic
multiplication operand (*) has to be escaped to prevent the shell from interpreting it as the filcnam,
metacharacter. Since expr can handle only integers, division yields only the integral part.
expr is often used with command substitution to assign a variable. For example, you can stti
variable z to the sum of two numbers:
$ x=6 y=Z; z=·expr $x + $y·
$ echo $z
8
Perhaps the most common use of expr is in incrementing the value of a variable. All programming
languages have a shorthand method ofdoing that, and it is natural that UNIX should also have its own:
$ x•S
$ x=·expr $x + 1• This is the same as C's x++
$ echo $x
6
If you are using the Korn shell or Bash~ then you can turn to Section 21.5 for a discussion on the
let statement that both shells use to handle computation.
14.9.2 String Handling
Though expr's s_tring handling facilities aren't exactly elegant, Bourne shell users hardly have any
choice. For manipulating strings, expr uses two expressions separated by a colon. The string to ht
worked upon is placed on the left ofthe:, and a regular expression is placed on its right. Depending
on the composition of the expression, expr can perform thre~ importa!lt string functions:
Essential Shell Programming

• Determine the length of the string.


• Extract a substring.
~- Locate the position of a character in a string.
The Length ofa String The length of a string is a ·relatively simple matter; the regular expression
'.* signifies to' expr that it has to print the number of characters matching the pattern, i.e., the
length of the.entire string: •
$ expr •abcdefg.htjkl" ·: '. *' Space on either side of: required
12 ';
. .
Herc, expr has counted the number of occurrences ·of any character(.*}. This feature is useful in
validating data entry. Consider that you want to validate the name of a person accepted through
the keyboard so that it doesn't exceed, say, 20 characters in length. The following expr sequence
can be quite useful for this task:
. . echo always returns true
while echo "Enter your name: \c" ; do
read name
if [ ·-expr •$name• I *' .. -gt 20 ] then
echo •Name too long•
else
break break terminates a loop
fi
done -
Extracting a Substring expr can extract a string enclosed by the escaped characters \ ( and \). If
you wish to extract the 2-digit year from a 4-digit string, you must create a pattern group and
extract it this way:
$ stg•2003
$ expr •$stg• : ' .. \ ( .. \) ' Extracts last two characters
03
Note the pattern group\( .. \). This is the tagged regular expression (TRE) used by sed (13.11.3),
but it is used here with a somewhat different meaning. It signifies that the first two characters in
the value of$stg have to be ignored and two characters have to be extracted from the third character
position. (There's no \1 and \2 here.)
Locating Position ofa Character expr can also return the location of the first occurrence of a
character inside a string. To locate the position of the character d in the string value of $stg, you
have to cou_nt the ~umber.of characters which arc not d ( ["d] *), followed by ad:
$ stg•abcdefgh; expr •$stg• : '[~d]*d'
4
~r duplicates some of the features of the test statement, and also uses the relational operators in
c same way. They arc not pursued here because test is a built-in feature of the shell, and is
nsequently faster. The Korn shell and Bash have built-in string handling facilities; they don't
expr. These features are taken up in Chapter 21.
l~!BJ UNIX: Concepts and Applications

14.10 $0: CALLING A SCRIPT BY DIFFERENT NAMES


In our discussion on links (11.Z.2), we raised the possibility of invoking a file by different nalllli
and doing different things depending on the name by which it is called. In fact, t~ere a~e a nurn~
of UNIX commands that do exactly that. Now that we know how to extract a stnng with expr,~
1 time we designed a single script! come.sh (Fig. 14.9), that compiles, edits or_ru~s the lasttnod~
C program. The script file will have three more names, but before developmg 1t, lets undc~ 1

the compiling mechanism used by the cc or gee compiler. .


A C program has the . c extension. When compiled with cc filename, it produces a file narn<d
a. out. However, we can provide a different name to the executable using the -o option, For instanq,
1 cc -o foo foo.c creates an executable named foo. We must be able to extract the "base" file~
after dropping the extension, and with expr it should be a simple matter.
. .
First, we store the name of the C program that was last modified, in .the variable l astfi le. Ncir,
we extract the base filename by dropping the . c extension using the TRE feat~re ofexpr. The CiSf
conditional now checks the name {saved in the variable comnand) by which the program is invoked,
Observe that the first option (rune) simply executes the value evaluated by the variable executablt
The only thing left to do now is to create three links:
ln come.sh come
ln come.sh rune
ln come.sh vie
Now you can run vi c to edit the program, come to compile it and rune to execute the object code.
: We'll only compile it here:
$ COIIC
hello.c compiled successfully
Note that ~s script works only with a C program that is stored, along with any functions, in one
file. If functions arc stored in separate files, this script won't work. In that case, make is the solutioa
and is discus~cd in Chapter 22.

#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

Enter the code and description: 04 antibiotics


Enter any more (y/n)? {Enter} No response, assumed to bey
Enter the code and description: 05 OTC drugs
Enter any more (y/n)? n
When you see the file newl i st, you'll know what you have achieved:
$ cat newlist
03lanalgesics
04lantibiotics
05IOTC drugs
Did redirection with /dev/tty achieve anything here? No, nothing yet, but after we make asnuj
change in the script, it will. Note that you added a record to newl i st with the >>symbol.This
causes newl i st to be opened every time echo is called up. The shell avoids such multiple fileopcninp
and closures by providing a redirection facility at the done keyword itself: .
done> newlist
Make this change in the script and remove the redirection provided with the» symbols. This fora
-,
of redirectio~ speeds' up execution time as newl ist is opened and closed only once. Because this
action redirects the standard output ofall commands inside the loop, we redirected some statcmcnu
to /dev/tty because we want their_output to come _co_the terminal. .

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

14.11.1 Using whH e to Wait for a File


Let's now consider an interesting while loop application. There arc' situations when a program
needs to read a file that is-created by another program, but it also has to wait until the file is created
The script, 110nitfi1 e. sh (Fig. 14.11), periodically monitors the disk for the existence of the file,
and then executes the program once the file has been located. It makes u~e of the external slet,
command tha~ makes the script pause for the duration (in seconds) as specified in its argument.
The loop executes repeatedly as long as the file invoice.1st can't be read ( f -r means not readable).
If the file becomes readable, the loop is terminated and the program a11 oc. p1 is executed. This
script is an ideal candidate to be run in the background li~c this:
monitfile.sh &
We used the s1eep command to check every 60 seconds for the existence of the file. s1eep is aho
quite useful in intro~ucing some delay in shell scripts.
Essenti~/ Shell Progr..amming 2911

#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 ;

14.11.2 Setting Up an Infinite Loop


Suppose y'au, as system administrator, want to see the free space available on your disks every five
minutes. You need an infinite loop, and it's best implemented by using true as a dummy control
command with wht 1e. true does nothing except return a true exit status. Another command named
false returns a false value: You can set up this loop in the background as well:
·whi 1e true ; do This fonn is also pennitted
, df -t df reports free space on disk
sleep 300
done &• & after done runs loop in backgroimd
With the job now running in the background, you can continue your other work, except that every
five minutes you could find your screen filled with df output (15.6.1). You can't now use the interrupt
key to kill this loop; you'll have to use ki 11 $!, which kills the last background job (9.9.1).

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.

14.12 for: LOOPING WITH A LIST


The shell's for loop differs in structure from the ones used in other programming languages. _
There is no three-part structure as t,lSed in C, awk and perl. Unlike while and until, for doesn't
test a condition, but uses a list instead:
for variahle in list
do
t:0mmands Loop body
done
The loop body also uses the keywords do and done, but the additional parameters her~ are variable
and list. Each whitespace-separated word in list is assigned to variable in turn, and commands arc
executed until list is exhausted. A simple example can help you understand things better:
l292l UNIX: Concepts and Applica tions
""--I

$ for file in chap20 chap21 chap22 chap23 do


> cp $file ${file}.bak
> echo $file copied to $file.b ak
> done
chap20 copied to chap20.bak
chap21 copied to chap21.bak
chap22 copied to chap22.bak
chap23 copied to chap23.bak
The list here comprises a series of charact er strings (c hap2O and onward s, representing
filcnal\l(\j
separated by whitespace. Each item in the 'list is assigne d to the variabl e file. file first
gets the
value chap20, then chap21, and so on. Each file is copied with a . bak extensi on and the complctiot
message displayed after every file is copied.

14.12.1 Possible Sources of the List


The list can consist of practically ~ny of the express ions that the shell underst ands and
proccssa.
for is probably the most often used loop in the UNIX system, and it's importa nt that you undersun!
it thoroughly.
List from Variables You can use a series ofvariables in the comma nd line. They are evaluated~
the shell before executing the loop:
$ for var in $PATH $HOME $MAIL; do echo •$var• ; done
/bin:/u sr/bin: /home/ local/b in:/usr /bin/X ll:.:/or acle/bi n
/home/henry
/var/ma il /henry
You have to provide the semicolons at the right places ifyou want to enter the entire loop
in asingk
line. The three output lines represent the values of the three environ ment variables.
List from Command Substitution , You can also use comma nd substitu tion to create ·the list The
following for command line picks up its list from the file cl is t:
for file in ·cat clist·

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.

14.12.2 basenarne: Changing Filename Extensions


We'll discuss yet another external command, basename, only because it's most effective when used
inside a for loop. Working in tandem/ they are quite useful in changing the extensions of a group
of filenames. basenue extracts the "btise" filename from an absolute pathname:
I
$ basenuie /hae/henry/project3/dec2bin.p1
decZbin.pl . . / .
When basenae lS used wath two argurcnts, it smps offthe second argument from the first argument:
$ basen111e ux2nd.txt txt
ux2nd. txtstripped off
You can now use this feature to rename
I
filename extensions from txt to doc:
I

You might also like