- A shell script is a collection of Unix commands, with a little bit of programming syntax thrown in to make it all work
- A comment begins with '#' and can start anywhere on the line
- The first line of the script tells what shell to use to interpret the script, in the form '#!/bin/sh'
- A really simple example [/bin/true from OpenBSD]:
#! /bin/sh
# $OpenBSD: true.sh,v 1.2 1996/06/26 05:41:53 deraadt Exp $
exit 0
- Why we're doing Bourne Shell instead of bash, zsh, csh, etc.
- System scripts should be written in Bourne shell, unless you know another shell will be available (Bourne shell, or a shell that is Bourne compatible, is available on anything that looks like Unix).
- Other "Bourne-like" shells (ksh, bash, zsh, etc.) have "extra" features that can be handy, but only use those shells if you can count on having them around (check your system -- probably all of the system scripts are written for /bin/sh).
- The C-shell has some very serious flaws and should be avoided for programming.
- Variables are assigned with the syntax var=something
- Note the lack of spaces
- If you do have a space, e.g. var = something, the shell will think you're trying to run the command var, with arguments = and something.
- Variables are referenced by using a "$" in front when referencing the value.
- If there is any confusion about what the variable name is, you can enclose it in braces like ${var}.
- Example:
var="This is a variable"
var1="This is var1"
echo $var
echo $var1
echo ${var}1
This is a variable
This is var1
This is a variable1
- There are three main types of quotes
- Single quote (' -- next to the semicolon on most keyboards)
- Anything in single quotes is taken literally, not interpreted, and gathered into one argument
- Double quote (" -- next to the semicolon, use the shift key)
- Anything in double quotes is interpreted, and gathered into one argument
- Backquotes (` -- next to 1 on most keyboards)
- Anything in backquotes is taken as a shell command, and the output of the command is placed on the command line in the place of the backquoted text
- Example
today=`date`
echo 'Running on `uname`: The value of today is $today.'
echo "Running on `uname`: The value of today is $today."
Running on `uname`: The value of today is $today.
Running on Linux: The value of today is Tue Feb 22 18:19:27 PST 2000.
- There are some special variables that a pre-defined for you whenever a shell script is running.
- $0
- $0 is the name of the currently running script, as specified to the system
- If you run the script with "./script", then $0 will be "./script"
- If you run the script with "/home/joeblow/bin/script", then $0 will be "/home/joeblow/bin/script"
- $#
- The number of arguments that were given to the script
- $*
- All of the arguments together, but be careful, because you lose quoting (see below)
- $1, $2, $3, ... $9
- $n is the nth argument, and is undefined if there was no nth argument
- The shift command moves every argument "down a notch"
- $2 becomes $1
- $3 becomes $2
- $4 becomes $3
- etc.
- (The value of the previous $1 is discarded)
- $$
- The PID of the script
- Often used to assign unique temporary filenames
- $?
- The exit value of the last command run from the script
- Example
echo "\$$ is '$$'"
echo "\$0 is '$0'"
echo "\$# is '$#'"
echo "\$* is '$*'"
echo "\$1 is '$1'"
echo "\$2 is '$2'"
echo "\$3 is '$3'"
shift
echo "after shift...."
echo " \$* is '$*'"
echo " \$# is '$#'"
echo " \$1 is '$1'"
echo " \$2 is '$2'"
echo " \$3 is '$3'"
With the command: ./vartest one "two three" four 'five six'
$$ is '11300'
$0 is './vartest'
$# is '4'
$* is 'one two three four five six'
$1 is 'one'
$2 is 'two three'
$3 is 'four'
after shift....
$* is 'two three four five six'
$# is '3'
$1 is 'two three'
$2 is 'four'
$3 is 'five six'
if <<condition>>
then
<<one or more statements>>
fi
if [ $var -eq 0 ]
then
echo "Don't try to divide by \$var!"
fi
- More on what goes in "<<condition>>" in a minute
- if-then-else
if <<condition>>
then
<<one or more statements>>
else
<<one or more statements>>
fi
if [ $var -eq 0 ]
then
echo "Don't try to divide by \$var!"
else
echo "It's ok to divide by \$var."
fi
if <<condition>>
then
<<one or more statements>>
elif <<condition>>
<<one or more statements>>
else
<<one or more statements>>
fi
if [ $var -eq 0 ]
then
echo "Don't try to divide by \$var!"
elif [ $var -eq 1 ]
echo "There's no point in dividing by \$var."
else
echo "It's ok to divide by \$var."
fi
- Functionally equivalent to if-then-elif-elif-elif-else-fi
- But cleaner-looking
- Matching of cases is done as strings using shell pattern matching (what you'd use with the ls command, for example).
- Syntax
case $var in
<<pattern1>>)
<<things to do for pattern 1>>
;;
<<pattern2>>)
<<things to do for pattern 1>>
;;
*) # The default condition
<<things to do in the default case>>
;;
esac
- Example (/etc/rc.d/init.d/misc on my old laptop)
#!/bin/sh
. /etc/rc.d/init.d/functions
# See how we were called.
case "$1" in
start)
echo -n "Starting misc daemons: "
daemon /usr/local/sbin/wwwoffled
echo
;;
stop)
echo -n "Stopping misc daemons: "
killproc wwwoffled
echo
;;
status)
# this is way overkill, but at least we have some status output...
echo "Status of misc daemons"
status wwwoffled
;;
restart|reload)
# do not do anything; this is unreasonable
$0 stop; $0 start
;;
*)
# do not advertise unreasonable commands that there is no reason
# to use with this device
echo "Usage: misc {start|stop|status|restart|reload}"
exit 1
esac
exit 0
for var in <<item1>> <<item2>> <<item3>>
do
<<statements -- some using $var>>
done
- Example (RedHat /etc/rc.d/init.d/killall)
#!/bin/sh
# Bring down all unneeded services that are still running (there shouldn't
# be any, so this is just a sanity check)
for i in /var/lock/subsys/*; do
# Check if the script is there.
[ ! -f $i ] && continue
# Get the subsystem name.
subsys=${i#/var/lock/subsys/}
# Bring the subsystem down.
if [ -f /etc/rc.d/init.d/$subsys.init ]; then
/etc/rc.d/init.d/$subsys.init stop
else
/etc/rc.d/init.d/$subsys stop
fi
done
while <<condition>>
do
<<statements>>
done
until <<condition>>
do
<<statements>>
done