Public feed
#379571
Shell Programming
Shell programmers and the dinosaur cloners of Jurassic Park have much in
common. They dont have all the pieces they need, so they fill in the missing
pieces with random genomic material. Despite tremendous self-confidence
and ability, they cant always control their creations.
Shell programs, goes the theory, have a big advantage over programs written
in languages like C: shell programs are portable. That is, a program
written in the shell programming language can run on many different flavors
of Unix running on top of many different computer architectures,
because the shell interprets its programs, rather than compiling them into
machine code. Whats more, sh, the standard Unix shell, has been a central
part of Unix since 1977 and, thus, we are likely to find it on any machine.
Lets put the theory to the test by writing a shell script to print the name
and type of every file in the current directory using the file program:
Date: Fri, 24 Apr 92 14:45:48 EDT
From: Stephen Gildea <gildea@expo.lcs.mit.edu>
Subject: Simple Shell Programming
To: UNIX-HATERS
Shell Programming 155
Hello, class. Today we are going to learn to program in sh. The
sh shell is a simple, versatile program, but we'll start with a basic
example:
Print the types of all the files in a directory.
(I heard that remark in the back! Those of you who are a little familiar
with the shell and bored with this can write start an X11 client on
a remote machine for extra credit. In the mean time, shh!)
While we're learning to sh, of course we also want the program we
are writing to be robust, portable, and elegant. I assume you've all
read the appropriate manual pages, so the following should be trivially
obvious:
file *
Very nice, isnt it? A simple solution for a simple problem; the *
matches all the files in the directory. Well, not quite. Files beginning
with a dot are assumed to be uninteresting, and * wont match them.
There probably arent any, but since we do want to be robust, well
use ls and pass a special flag:
for file in `ls -A`
do
file $file
done
There: elegant, robust... Oh dear, the ls on some systems doesnt
take a -A flag. No problem, we'll pass -a instead and then weed out
the . and .. files:
for file in `ls -a`
do
if [ $file != . -a $file != .. ]
then
file $file
fi
done
Not quite as elegant, but at least its robust and portable. Whats that?
ls -a doesnt work everywhere either? No problem, we'll use ls -f
instead. Its faster, anyway. I hope all this is obvious from reading
the manual pages.
156 csh, pipes, and find
Hmm, perhaps not so robust after all. Unix file names can have any
character in them (except slash). A space in a filename will break this
script, since the shell will parse it as two file names. Well, thats not
too hard to deal with. We'll just change the IFS to not include Space
(or Tab while we're at it), and carefully quote (not too little, not too
much!) our variables, like this:
IFS='
'
for file in `ls -f`
do
if [ "$file" != . -a "$file" != .. ]
then
file "$file"
fi
done
Some of you alert people will have already noticed that we have
made the problem smaller, but we haven't eliminated it, because
Linefeed is also a legal character in a filename, and it is still in IFS.
Our script has lost some of its simplicity, so it is time to reevaluate
our approach. If we removed the ls then we wouldnt have to worry
about parsing its output. What about
for file in .* *
do
if [ "$file" != . -a "$file" != .. ]
then
file "$file"
fi
done
Looks good. Handles dot files and files with nonprinting characters.
We keep adding more strangely named files to our test directory, and
this script continues to work. But then someone tries it on an empty
directory, and the * pattern produces No such file. But we can add
a check for that
at this point my message is probably getting too long for some of
your uucp mailers, so I'm afraid I'll have to close here and leave fixing
the remaining bugs as an exercise for the reader.
Stephen
Shell Programming 157
There is another big problem as well, one that weve been glossing over
from the beginning. The Unix file program doesnt work.
Date: Sat, 25 Apr 92 17:33:12 EDT
From: Alan Bawden <Alan@lcs.mit.edu>
Subject: Simple Shell Programming
To: UNIX-HATERS
WHOA! Hold on a second. Back up. You're actually proposing to
use the file program? Everybody who wants a good laugh should
pause right now, find a Unix machine, and try typing file * in a
directory full of miscellaneous files.
For example, I just ran file over a directory full of C source code
here is a selection of the results:
arith.c: c program text
binshow.c: c program text
bintxt.c: c program text
So far, so good. But then:
crc.c: ascii text
See, file isnt looking at the .c in the filename, its applying some
heuristics based on an examination of the contents of the file. Apparently
crc.c didnt look enough like C codealthough to me it
couldnt possibly be anything else.
gencrc.c.~4~: ascii text
gencrc.c: c program text
I guess I changed something after version 4 that made gencrc.c look
more like C
tcfs.h.~1~: c program text
tcfs.h: ascii text
while tcfs.h looked less like C after version 1.
time.h: English text
Thats right, time.h apparently looks like English, rather than just
ascii. I wonder if file has recognition rules for Spanish or French?
158 csh, pipes, and find
(BTW, your typical TeX source file gets classified as ascii text
rather than English text, but I digress
)
words.h.~1~: ascii text
words.h: English text
Perhaps I added some comments to words.h after version 1?
But I saved the best for last:
arc.h: shell commands
Makefile: [nt]roff, tbl, or eqn input text
Both wildly wrong. I wonder what would happen if I tried to use
them as if they were the kinds of program that the file program
assigns them?
Alan