CSE 80 -- Lecture 7 -- Jan 28


Today I completed the shell script argument parsing example:
#!/bin/sh
usage="Usage: die [-x exitstatus] [-k killsig]"
while :
do
	case $# in
	0)	break;;
	*)	case "$1" in
		-x)	case $# in
			1)	echo "die:  -x requires exit status argument
$usage" >&2; exit 1;;
			esac
			stat="$2"; ; ;;
		-k)	case $# in
			1)	echo "die:  -k requires signal number argument
$usage" >&2; exit 1;;
			esac
			killsig="$2"; ; ;;
		# -v)	verbose=1; ;;	# example of a flag that takes
						# no arguments
		-*)	echo "Illegal flag $1
$usage" >&2; exit 1;;
		*)	break;;			# filename arguments
		esac
	esac
done

case "$stat" in
'')	case "$killsig" in
	'')	echo "die: You must have one of -x or -k
$usage" >&2; exit 1;;
	*)	kill -"$killsig" $$;;
	esac
	;;
*)	case "$killsig" in
	'')	exit $stat;;
	*)	echo "die: You must have one of -x or -k
$usage" >&2; exit 1;;
	esac
	;;
esac

exit

# example of handling filename arguments
for fn
do
	: # commands that process $fn
done

Another Script Example and sed

I talked about my porting all of my scripts from my ``home'' Solaris workstation to the the class account, and using the commands:
$ cd ../public/bin
$ mkdir ../bin.x
$ for i in *
> do
>  < $i sed 's,/home/bsy/bin,/home/solaris/ieng9/cs80w/public/bin,g' > ../bin.x/$i
>  chmod +x ../bin.x/$i
> done
$
interactively. After checking things out, I removed the original bin directory and renamed bin.x to bin. (BTW, what's the > prompt?)

The sed command used here is the Stream Editor. Its basic, implicit loop is to read in a line of input, apply whatever editing commands are applicable, and then print out the possibly modified input. In the above example, the editing command is to substitute /home/solaris/ieng9/cs80w/public/bin for occurrences of /home/bsy/bin in the input line. The g at the end specifies that this substitution is to be done globally over all occurrences in that line; without the g, only the first occurrence of /home/bsy/bin would be replaced.

sed's default action of printing can be suppressed with the -n flag, whereupon output is performed only when explicitly told to via the editing command p. You should skim through the sed manual page -- you can ignore the complexities of the hold space versus the pattern space for now, but you should know the s, p, d, a, i, c, and q commands.

I gave the following examples, based on the fact that an empty line always separates the email headers and the email body (RFC-822):

Print out email headers:

$ sed -n '1,/^$/p' < email.file
and automatically adding a salutation:
$ sed '/^$/a\
Hello' < email.file
Note: this example doesn't work quite right. Why? (Hint: what might the body look like / contain?)

awk

Awk is a programming language with strong C flavors in its syntax but oriented towards pattern matching. Like sed, awk has an implicit loop, where input is processed one line at a time. The input is matched against guard patterns or expressions on rules, which are fired or executed if the match succeeds. The general form of an awk program is the following:
BEGIN {
	cmds; /* A */
}
/pattern/ {
	cmds; /* B */
}
NR == 4 {
	cmds; /* C */
}
END {
	cmds; /* D */
}
The A cmds controlled by the BEGIN pseudo pattern is run before any input are processed; as you may have guessed, the D commands are run after all input are processed. The B and C commands are run when their guards are satisfied: for B, that means that the regular expression pattern matches the input line, and for C, that means the boolean expression is true, i.e., the number of input records so far equals 4 (at the 4th line).

The control flow constructs of awk are very similar to those of C: there are if / else statements, while statements, and for loops just like in C. The data types are different though: objects are not really typed, and the awk program will dynamically convert a string into a number and vice versa:

bash$ awk 'END { print "5" + 2; }' < /dev/null
7
bash$

One area in which awk and C are very different is the kinds of aggregate types available. awk has no struct or union, but has instead an associative array. Instead of the indices being only numbers, they may be arbitrary strings. For example:

#!/software/common/gnu/bin/bash
#
# take input a file containing two fields per line:  login name and
# number of points
#
awk '
{
	grade[$1] = $2
}
END {
	for (student in grade) {
		print student, grade[student]
		sum += grade[student];
		nstud++;
	}
	print "Mean grade " sum / nstud
}' < ${1:-"/home/solaris/ieng9/cs80w/cs80w/grade.db"}
uses an associative array to store the login/points tuple.
[ CSE 80 | ACS home | CSE home | CSE calendar | bsy's home page ]
picture of bsy

bsy@cse.ucsd.edu, last updated Tue Mar 18 15:49:30 PST 1997.

email bsy