Scripting Clinic: The Bash Continues

By Carla Schroder | Mar 16, 2004 | Print this Page
http://www.enterprisenetworkingplanet.com/netsysm/article.php/3326771/Scripting-Clinic-The-Bash-Continues.htm
Last month we took a tour of some Bash features, and learned that scripting is really just plumbing — a flexible, powerful way to combine Linux commands to accomplish complex tasks.

It's not uncommon, when discussing Bash scripting, for lordly Perl geeks to sniff disdainfully, and make helpful comments like "Your little 20-line Bash script is so amusing. I can do the same thing in Perl in a mere 300 lines." Or you get the hardbitten old C coders who think scripting of any kind is sissy — real geeks compile. Pay them no heed. Bash scripts are perfectly good tools.

Don't forget that "Linux In A Nutshell" is a great Bash reference, as well as the best all-around Linux reference. It's helpful to have all the Bash commands and keywords neatly separated from other Linux programs and commands. Much confusion in Linux comes from not understanding what belongs to what and this book keeps everything in its place.

Sha Bang
You've probably been told that it's better to use #!/bin/sh, which calls the Bourne shell, instead of #!/bin/bash, to make your scripts more portable. Well ya know, that's a personal decision. I've never touched a Unix box in my life, and chances are I never will. You lose some Bash functionality with #!/bin/sh. There's no point in preparing for some vaguely possible future; use what makes sense. If you're using Bash, write Bash scripts. If you prefer some other shell, go for it, and quit pestering Bash users.

Whichever one you elect to use, always be sure to make it the very first line of your scripts.

For/Do Loops
For/Do loops are great for performing batch operations on piles of files. The syntax is simple:

for variable-name in directory
do stuff
done

The Bash keywords are for, in, do, and done. Remember to not use these words for anything else, because they are reserved Bash words. (man bash has a list of reserved words.)

For example, suppose you want to convert a batch of audio files with the sox utility. sox does not have a built-in batch function, so Bash does the heavy lifting. This command converts all the .wav files in the current directory to .ogg format:

$ for i in *.wav ; do echo $i ; sox $i ${i%%.wav}.ogg ; echo ${i%%.wav}.ogg; done

Ok let's take this piece by piece.

$ for i in *.wav ;

i is the variable name that represents each filename, in turn, selected by the *.wav expression. *.wav means all files ending in .wav. It is traditional to use i for iterations. You can use almost anything- any letter of the alphabet, words, your name, whatever you want, as long as it is not a Bash reserved word. The semicolon ends the for statement. for will take each *.wav filename in turn, and pass it to

do echo $i;

All this does is print the output of $i, which is each filename in turn, to the screen. This lets you see what the command is doing. There can be only one for statement, and one do statement. But you can add any number of semi-colon delimited commands afterwards:

sox $i ${i%%.wav}.ogg ;

This is the meat of our little for loop. sox takes each .wav file, one at a time, and converts them to .ogg files. ${i%%.wav}.ogg means "remove the .wav extension." Without this, the files will be named soundfile.wav.ogg, instead of soundfile.ogg.

echo ${i%%.wav}.ogg;

This prints the converted filenames to the screen.

done;

All finished! Because we can line up any number of commands after the the do statement, we need to tell for/do when their work is done.

Continued on Page 2: Cleaning Up
Continued From Page 1

Cleaning Up
The above command dumps everything in the working directory, so your new files are jumbled up with your old files. Why not do a little housecleaning:

$ mkdir ~/oggfiles
$ mv *.ogg ~/oggfiles

If/Else
Conditional statements are invaluable in scripting. This is the most common style of usage:

if
    command was successful
then
    go to the next step
else
    make corrections
fi
    all done

Here's an example from last month's Scripting Clinic:

if [ -e $HOME/1backups ]
then
echo "The backup directory exists."
else
echo "A backup directory does not exist, and will be created."
mkdir $HOME/1backups
echo "A backup directory has been created"
fi

This has all the elements of a good, useful conditional statement. It tests for the existence of a backup directory, then creates one if the directory does not exist. It prints a running commentary to the screen, so you know what's going on. It even has a bit of error-checking, and informs you if the backup succeeded, or failed:

if [ -e $BACKUPDIR/$ARCHIVENAME ]
then
echo "Jolly good show, the backup worked!"
else
echo "Dagnabit, the backup failed. Time to debug."
fi

It is better to not fail or succeed silently, always echo some kind of confirmation.

A common source of confusion is the $, or variable substitution. $ means "substitute the value of the following variable." So mkdir $HOME/1backups does not create a directory named HOME/1backups; it substitutes the home directory of the user running the script. HOME is a builtin Bash variable. You can test this yourself:

$ echo $HOME
/home/carla

Putting It All Together

#!/bin/bash
# a simple script for converting wav files
# to ogg format, and moving the ogg files
# into a separate directory
# first, our directory variables
OGGDIR=~/TMP/oggfiles
WAVDIR=~/1wavtest

#change to directory the wavs are in
cd $WAVDIR

# use sox for the file conversion
# all wavs in the directory are selected
for i in *.wav ;
do echo $i ;
sox $i ${i%%.wav}.ogg ;
echo ${i%%.wav}.ogg;
done

#check to see if the ogg directory exists
if [ -e $OGGDIR ]
then
echo "The ogg directory exists, and the ogg files will be moved into it."
mv *.ogg $OGGDIR

# create it if it's not already present
else
echo "The ogg directory does not exist, so I will create it."
mkdir -p $OGGDIR
mv *.ogg $OGGDIR
echo "All finished, enjoy your music."
fi

Pay no heed to mean people who criticize your scripts, and call them names like "kludgy" and "inelegant." There are many ways to do even the simplest tasks in Linux. If it works, it is good. However, it's often helpful to share your scripts on mailing lists, and put other people to work refining and improving them. Let 'em be snarky, as long as they are helpful.

Don't miss next month's Scripting Clinic, where we shall introduce ourselves the powerful, yet understandable, Python.

Resources
man bash
Linux In A Nutshell, by Ellen Siever
Scripting Clinic: Have a Bash with This Linux Shell
BASH Programming - Introduction HOW-TO

» See All Articles by CrossNodes contributor Carla Schroder