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 famil- iar 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 trivi- ally 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 fix- ing the remaining bugs as an exercise for the reader. Stephen