Public feed #379571

Something

public text v1 · immutable
Raw Edit
·published 2007-03-03 10:52 UTC
rendered paste body
Shell Programming
Shell programmers and the dinosaur cloners of Jurassic Park have much in
common. They don’t have all the pieces they need, so they fill in the missing
pieces with random genomic material. Despite tremendous self-confidence
and ability, they can’t 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. What’s 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.
Let’s 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, isn’t 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 * won’t match them.
There probably aren’t any, but since we do want to be robust, we’ll
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 doesn’t
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 it’s robust and portable. What’s that?
“ls -a” doesn’t work everywhere either? No problem, we'll use “ls -f”
instead. It’s 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, that’s 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 wouldn’t 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 we’ve been glossing over
from the beginning. The Unix file program doesn’t 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’ isn’t looking at the “.c” in the filename, it’s applying some
heuristics based on an examination of the contents of the file. Apparently
crc.c didn’t look enough like C code—although to me it
couldn’t 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
That’s 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