KokoroMix |
10-07-2006 12:42 AM |
Code:
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
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.
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.
Code:
Error Codes and Error Checking
Our programming example glossed over how the file command reports an
error back to the shell script. Well, it doesn’t. Errors are ignored. This
behavior is no oversight: most Unix shell scripts (and other programs as
well) ignore error codes that might be generated by a program that they
call. This behavior is acceptable because no standard convention exists to
specify which codes should be returned by programs to indicate errors.
Perhaps error codes are universally ignored because they aren’t displayed
when a user is typing commands at a shell prompt. Error codes and error
checking are so absent from the Unix Canon that many programs don’t
even bother to report them in the first place.
Date: Tue, 6 Oct 92 08:44:17 PDT
From: Bjorn Freeman-Benson <bnfb@ursamajor.uvic.ca>
Subject: It’s always good news in Unix land
To: UNIX-HATERS
Consider this tar program. Like all Unix “tools” (and I use the word
loosely) it works in strange and unique ways. For example, tar is a
program with lots of positive energy and thus is convinced that nothing
bad will ever happen and thus it never returns an error status. In
fact, even if it prints an error message to the screen, it still reports
“good news,” i.e., status 0. Try this in a shell script:
tar cf temp.tar no.such.file
if( $status == 0 ) echo "Good news! No error."
and you get this:
tar: no.such.file: No such file or directory
Good news! No error.
I know—I shouldn’t have expected anything consistent, useful, documented,
speedy, or even functional…
Bjorn
Code:
My judgment of Unix is my own. About six years ago (when I first got
my workstation), I spent lots of time learning Unix. I got to be fairly
good. Fortunately, most of that garbage has now faded from memory.
However, since joining this discussion, a lot of Unix supporters
have sent me examples of stuff to “prove” how powerful Unix is.
These examples have certainly been enough to refresh my memory:
they all do something trivial or useless, and they all do so in a very
arcane manner.
One person who posted to the net said he had an “epiphany” from a
shell script (which used four commands and a script that looked like
line noise) which renamed all his '.pas' files so that they ended with
“.p” instead. I reserve my religious ecstasy for something more than
renaming files. And, indeed, that is my memory of Unix tools—you
spend all your time learning to do complex and peculiar things that
are, in the end, not really all that impressive. I decided I’d rather
learn to get some real work done.
—Jim Giles
Los Alamos National Laboratory
Code:
Date: Thu, 28 Jun 1990 18:14 EDT
From: pgs@crl.dec.com
Subject: more things to hate about Unix
To: UNIX-HATERS
This is one of my favorites. I’m in some directory, and I want to
search another directory for files, using find. I do:
po> pwd
/ath/u1/pgs
po> find ~halstead -name "*.trace" -print
po>
The files aren’t there. But now:
po> cd ~halstead
po> find . -name "*.trace" -print
./learnX/fib-3.trace
./learnX/p20xp20.trace
./learnX/fib-3i.trace
./learnX/fib-5.trace
./learnX/p10xp10.trace
po>
Hey, now the files are there! Just have to remember to cd to random
directories in order to get find to find things in them. What a crock of
Unix.
Poor Halstead must have the entry for his home directory in /etc/passwd
pointing off to some symlink that points to his real directory, so some commands
work for him and some don’t.
Why not modify find to make it follow symlinks? Because then any symlink
that pointed to a directory higher up the tree would throw find into an
endless loop. It would take careful forethought and real programming to
design a system that didn’t scan endlessly over the same directory time
after time. The simple, Unix, copout solution is just not to follow symlinks,
and force the users to deal with the result.
Code:
Say you are a novice user with two files in a directory, A.m and B.m.
You’re used to MS-DOS and you want to rename the files to A.c and B.c.
Hmm. There’s no rename command, but there’s this mv command that
looks like it does the same thing. So you type mv *.m *.c. The shell
expands this to mv A.m B.m and mv overwrites B.m with A.m. This is a
bit of a shame since you had been working on B.m for the last couple of
hours and that was your only copy.
Code:
Sysadmins manage a large assortment of configuration files. Those allergic
to Microsoft Windows with its four system configuration files shouldn’t
get near Unix, lest they risk anaphylactic shock. Unix boasts dozens of
files, each requiring an exact combination of letters and hieroglyphics for
proper system configuration and operation.
Each Unix configuration file controls a different process or resource, and
each has its own unique syntax. Field separators are sometimes colons,
sometimes spaces, sometimes (undocumented) tabs, and, if you are very
lucky, whitespace.
Code:
“Two of the most famous products of Berkeley are LSD and Unix. I
don’t think that this is a coincidence.”
—Anonymous
|