An introduction to bourne shell scripting
The bourne shell appears to be the most widely distributed shell available on unix-based systems. While it is not as powerful as newer shells such as bash (bourne again shell) or csh, we will write bourne shell scripts for their portability and its compatability with many modern shells.
A bourne shell script is simply a plain text file that contains commands you might normally run in the bourne shell in addition to a few other useful constructs we are famililar with from C and C++. In this document, we will demonstrate each of the following features with simple bourne shell script examples:
- Command line arguments
- String concatenation
- Variable assignment and usage
- Conditionals
- Loops
For a more complete reference please see the Bourne Shell Programming page provided by ooblick.com.
Hello World
#!/bin/sh
echo "Hello world"
The first line, #!/bin/sh
, tells the operating system that the file should be
processed with the executable /bin/sh. If this line is omitted, then whatever
script is responsible for launching the shell script will execute the commands.
On the second line we tell the program to invoke the external command echo
with the argument “Hello world”.
Save the above as example.sh
and perform the following tasks (you’ll want to
follow this proceedure for all examples):
- Execute the command
chmod +x example.sh
to mark the script as executable - Run the script by typing the command
./example.sh
You can also run the script via the command sh hello.sh
. Doing so does not
require the script to be executable, and will ignore the #!/bin/sh
line at
the top of the script. This is useful if you want to run the script in a
different shell wihtout modifing the script contents: bash hello.sh
.
Command line arguments and string concatenation
#!/bin/sh
echo "All arguments: $*" # Doesn't include the script name
echo "Script name: $0" # Output the script name
echo "First argument: $1" # Output the first command line argument
echo "Num args: $#" # Output the number of arguments
If you run the above script via example.sh a b c
you should see the following
output:
All arguments: a b c
Script name: ./example.sh
First argument: a
Num args: 3
Take note that $*
doesn’t include the script name $0
and that this argument
is not counted in $#
. This differs from the int argc, char *argv[]
parameters for C/C++ programs which do count argv[0] as an argument. Also
notice that we are referring to variables from within the strings, in spite of
whatever type that variable holds.
Variables and capturing external commands’ output
#!/bin/sh
msg_string="Path to script:"
prog_name=$(basename $0) # Assign the output of the command "basename $0"
# to the variable prog_name
cur_directory=$(pwd) # Do something similiar with pwd
echo "$msg_string: $cur_directory/$prog_name"
In the above example, we see three examples of variable assignment. On line
two, we directly assign a string to the variable msg_string
. Note that we do
not use $
when assigning a value to a variable. However, the $
must be used
when attempting to recall the stored value.
It is also important to know that the lack of whitespace around =
is very
important. An =
character surrounded by whitespace has a completely different
meaning (comparision) in bourne shell scripting.
On lines 3 and 5, we see the usage of $(COMMAND LINE). This syntax is used to
execute the command between the ( )
and capture its output. In this case, we
use the program basename
to convert ./example.sh
into example.sh
and the
program pwd
to tell us our present working directory. We store the results of
those commands in the variables prog_name
and cur_directory
respectively.
Finally, on line 6, we demonstrate another example of string concatenation, this time combining three separate variables into a single string.
Conditionals
#!/bin/sh
if [ $# -ne 0 ]; then
echo "There are command line arguments"
else
echo "There are no command line arguments"
fi
In the above example, we demonstrate the use of an if/else conditional on the
condition that the number of arguments does not equal zero. Again, please note
that whitespace is crucial in some places. Here the whitespace after [
and
before ]
are necessary.
In addition to the typical number comparision operators (eq
, ne
, gt
,
ge
, lt
, le
), bourne shell also allows for the testing of paths to check
if the path points to a regular file, -f
, a directory, -d
, or a few
others. In the following example, we confirm that our current directory, .
, is, in
fact, a directory and not a file.
#!/bin/sh
if [ -d . -a ! -f . ]; then
echo ". is in fact a directory and not a file."
else
echo "This should never be true."
fi
In the above conditional, I introduced the logical and, -a
, between two
independent conditions, -d .
and ! -f .
. The !
preceding the second
condition is the logical negation.
Basic Looping
#!/bin/sh
for filename in $(ls); do
echo $filename
done
In the above example, we loop over the list of filenames in the current
directory as reported by the command ls
. The variable filename
holds the
result of the current iteration.
This example is as much detail as I will provide on looping in bourne scripts as it should be all that’s needed for this class. I do, however, want to make a note that looping over files in this manor does not work if a filename has a space character in it. For more on this issue, please see this resource.