Shell Scripts

If you have a basic familiarity with unix, and you have a lot of work to do, then shell scripting can save you time. A shell script is just a text file full of commands. It must be executable. Although you may simply type in a bunch of commands that you would type at the prompt, there are some more sophisticated things that you probably have not done at the prompt (though you could). Different shells have different options and different syntax available for doing these more sophisticated things. This page will discuss bourne shell scripts since they are easy, and fast. This is just an introduction, you should look at the examples referenced at the end and read books about shell scripting, and look on the web to learn more.


The Simplest Case

Here is a very simple script. Create a file called fred and open it in a text editor. Type

ls -l

Save the file. Make sure everyone can execute the file:

>chmod ugo+x fred

Now type:

>./fred

This tells the shell to look in the current directory for fred and to treat fred as a command. You should see that the results of running fred are the same as running ls -l. Note, however, that unless fred is in a directory specified in your path, that you will always have to type the path to fred to run it.

You can type multiple commands into a shell script, just put one command on each line. Type some additions to fred (everything in red):

ls -l
mkdir sally
cd sally
touch harry sydney veronica

Now fred will tell you what files are in the current directory, then make a directory named sally, cd into sally and make 3 files in the directory (harry, sydney and veronica).

A nice addition is to call the shell you want at the top of the script. This just helps to prevent the script from blowing up if the system calls some shell other than what you expect. Perhaps this is voodoo, but it doesn't hurt anything and it tells you in the future which shell language the script uses:

#!/bin/sh

ls-l
mkdir sally
cd sally
touch harry sydney veronica

This script should do the same thing as it did before, but you have told it explicitly to use the bourne shell. The very first line of the script can be used for this sort of special call. You need not separate the call to sh from the rest of the commands, but it makes it look nicer.


Adding Comments

When you, or someone else, looks at your shell script in a year, it would be nice if you knew what it did, who created, it, when, why, and how to use it, simply by reading it. Good programmers make copious comments explaining these things. Any line preceded by # (other than the one that calls the interpreter, e.g., #!/bin/sh at the top of the script), will be ignored by the interpreter:

#!/bin/sh

#The above line calls sh to execute fred.

#fred makes a directory named sally and creates 3 files in that #directory: harry, sydney and veronica. 4/16/2002 DP.

#the next line lists the files in the current directory
ls -l

#The next line makes the directory "sally"
mkdir sally

#The next line cds into "sally"
cd sally

#The following line uses the "touch" command to make the files "harry", #"sydney" and "veronica".
touch harry sydney veronica


Positional Arguments

In the above script, there is only one way to change the names of the directory or files that fred creates: open the script and modify it.
But there is an alternative. You can use positional arguments. Positional arguments are typed in order after the command. The shell knows that the first word you type (e.g., fred) is the name of the command. It thinks of this as $0. The first argument to fred is $1. The next one is $2 etc. So, you could do the following to allow a single argument to fred:

#!/bin/sh

#The above line calls sh to execute fred.

#fred makes a directory that you name at the command line
# and creates 3 files, harry, sydney and veronica, in that directory.
#4/16/2002 DP.

#The next line makes the directory specified on the command line
mkdir $1

#The next line cds into that directory
cd $1

#The following line uses the "touch" command to make the files "harry", #"sydney" and "veronica".
touch harry sydney veronica

In the above script, fred is allowed to take a single command line argument, which it uses as a directory name. So if I type:

>fred Mary

fred will create a directory named Mary and put 3 files in it, harry, sydney and veronica.

More Positional Arguments

But we could do even more:

#!/bin/sh

#The above line calls sh to execute fred.

#fred makes a directory that you name at the command line
# and creates 3 files, that you name at the command line, in that directory.
#4/16/2002 DP.

#The next line makes the directory
mkdir $1

#The next line cds into that directory
cd $1

#The following line uses the "touch" command to make 3 files.
touch $2 $3 $4

Now fred takes 4 arguments:

>fred Office scott barb jen

The above command creates an Office directory and fills it with three files: scott, barb and jen. Although you have to type more at the command line, the script is more generalized, and thus more useful than our initial script.


Variable Names

Sometimes, you may want to repeat something over and over again in a script (like a pathname) but you would prefer to type it once. Here's a harmless example:

path="/data3/Dianne"
cd $path
pwd

This will cd to the specified directory (/data3/Dianne) and print out its location. As soon as the program ends, you will find yourself back where you started.)

Or a slightly different example:

path="/data3/Dianne"
cd $path"/temp"
pwd

This is like the above, but it allows you to specify some general pathname and then add to the path at the point of actually doing something. Thus pwd will tell you that you are in /data3/Dianne/temp.


Self Explanatory Scripts and If-Then Statements

If we run one of our last two programs without the necessary arguments (e.g., type fred Office scott), it will fail..and it won't do a very good job of telling you why it failed. Maybe a clever user would go find the script file and open it to read what it does...and thus figure the problem out. But, the kind thing to do, as a programmer is to provide a usage message, if the user goofs. Here is an example of an if-then statement that creates such a usage message if a user types fred without any arguments (or without enough arguments:

#!/bin/sh
#The above line calls sh to execute fred.
#fred makes a directory that you name at the command line
# and creates 3 files, that you name at the command line, in that directory.
#4/16/2002 DP.

if [ $# -lt 3 ]
then
echo "Usage: $0 filename filename filename"
echo "Example: $0 Mary f1 f2 f3"
echo "Comments: Create a directory (e.g., Mary) and fill it with the named files (e.g., f1 f2 f3)"
exit 1
fi

#The next line makes the directory
mkdir $1

#The next line cds into that directory
cd $1

#The following line uses the "touch" command to make 3 files.
touch $2 $3 $4

Explanation: We have added an if-then statement. Let's examine it in detail. It has the following structure:

if x # if condition x is true
then
#then do what comes on the next few lines, up until we exit successfully
y
#this is what you do if x is true
exit 1
#this says to exit the if-then statement with a value of "1" (successful)
fi
# this says the if-then statement is done

Here "x" is a complex expression: [ $# -lt 3 ]. $# means "number of arguments". -lt means "is less than". So the statement reads: If the number of arguments on the command line is less than 3.

"y" is 3 echo statements. Echo statements simply write out their contents to standard output (e.g., the screen).


For Loops

Looping is a very valuable tool in scripts and programming. A loop does something over and over again. A for loop does something over and over again until it reaches the end of a list.

#!/bin/sh
#The above line calls sh to execute fred.
#fred makes a directory that you name at the command line
# and creates 3 files, that you name at the command line, in that directory.
#4/16/2002 DP.

if [ $# -lt 3 ]
then
echo "Usage: $0 filename filename filename"
echo "Example: $0 Mary f1 f2 f3"
echo "Comments: Create a directory (e.g., Mary) and fill it with the named files (e.g., f1 f2 f3)"
exit 1
fi

#The next line makes the directory
mkdir $1

#The next line cds into that directory
cd $1

#The following line uses the "touch" command to make 3 files.
touch $2 $3 $4

for i in *
do
cp $i ..
done

Explanation: This is an extremely simple for-loop that copies everything it finds in the current directory up to the parent directory. Because we have already moved into the directory we created (e.g., Office), the for loop will find anything that meets the criterion (the criterion is *, so our three files scott, barb and jen all match the criterion). For each of these files, it'll assign it the variable name i and run the procedures inside the the do-done statement (in this case, copy that file up one level). Then it'll start the for loop over again and do the next file. When it has operated once on each file, it stops.


To see some example scripts that use the above techniques, go here and click on any script name. You will see if-then statements embedded in other if-then statements, and for loops that do weird things. As with any other programming task, it is very useful to try work from examples. You can find resources out on the net and in books devoted to the subject. You may also wish to see special notes on using backslashes for line continuation.