Shell Programming
Contents
Topics
Assignment
Shell Basics
A shell refers to the command interpreter that provides the working
environment on a Unix or Linux host. The shell can be used to execute
commands directly, or to write scripts.
We are going to discuss Shell programming, where Shell refers specifically
to the Bourne Shell. The Bourne Shell capabilities are included in
several variants including sh, bash, bsh, ksh and pdksh. The other
shell schemes are based on the C-shell, which is less capable and more
difficult to write scripts with.
Running Shell Programs
- ./script-name
- source script-name
- aliasing
Definitions
Bash, like most shells, has a number of control characters that have
special meaning.
Metacharacters, when unquoted, separate words. They are
|
| &
| ;
| (
| )
| <
| >
| space
| tab
|
Escaping, allows metacharacters to have their normal meaning.
For example,
\|     or     \>
Control operators perform some function. They are:
Reserved words are words that have special meaning and cannot be
used for user defined names:
!
| case
| do
| done
| elif
|
else
| fi
| for
| function
| if
|
in
| select
| then
| until
| {
| }
|
Bash Environment Variables
There are also a large number of predefined environment variables. Some
of the more important are:
HOME | Your home directory
|
HOSTTYPE | Returns the hosttype
|
IFS | The current separator used for word splitting
|
LINENO | Generates the current line in the script
|
MAIL | Mail spool path
|
OSTYPE | Returns the operating system
|
PATH | Search path for executables
|
PS1 | Login prompt
|
PWD | Current directory
|
RANDOM | Generates a random number when properly set
|
SHELL | Current shell
|
USER | Your user name
|
Shell Variables
Working With Shell Variables
- variable=value     -   set the value of a
variable
- No spaces around the "="
- CLASSPATH="/usr/java/jre/lib"
- PATH="/usr/bin:/usr/local/bin:/bin:/sbin:"
- COUNT=10
- export variable     -   make a variable globally
accessible to other processes
- $variable     -   access the value of a variable
- echo [options] args     -   output the
value of the args
- echo "The count is" $count     =>    
The count is 14
Script Basics
- #     -   comment remainder of the line
- The first line specifies the script interpreter
e.g. #! /bin/sh or #! /bin/bash
- Arguments - Each argument is numbered from $1 to $N, where there
are N arguments. For example,
mysilly_script myhome 8 bozo
$1 | = | "myhome"
|
$2 | = | 8
|
$3 | = | "bozo"
|
There are some special symbols
$@ | = | A list of all arguments, as quoted strings
|
$# | = | The number of arguments
|
$* | = | A list of all command line arguments.
|
Expansion
After a shell command is split into words, there are 7 levels of expansion
performed in the following order
brace
|
tilde
|
variable and parameter
|
command substitution
|
arithmetic
|
word splitting
|
pathname
|
Each of these provides particular properties as described briefly below.
This is a very large topic and to get complete understanding you need
to know more about the scripting lanaguage, so use the following as
an introduction and reference.
Just remember that all of these expansions are going on.
brace expansion
Generate arbitrary strings.
ls /usr/{src,lib,share} | =
| ls /usr/src; ls /usr/lib; ls/usr/share
|
chmod 644 /home/bozo/{public,d?t?.*} | =
| chmod 644 /home/bozo/public;
|
  |   | chmod 644 /home/bozo/d?t?.*
|
tilde expansion
If a word begins with a tilde ("~"), all characters preceding the
first slash ("/") are treated as a possible login name. If the
possible login name is null, it is replaced with the value of
the HOME environment variable, or if unset, the home directory
name of the user. If tilde is followed by +, then PWD is used
and if followed by a -, the OLDPWD is used. This expansion is also
executed on unquoted instances of : and =.
ls ~bozo | = | ls $HOME/bozo
|
ls ~zippo/net | = | ls /home/zippo/net
|
~+/files/prog | = | $PWD/files/prog
|
PATH=$PATH:~/bin | = | PATH=$PATH:$HOME/bin
|
variable and parameter expansion
A dollar sign ("$") introduces parameter, command and arithmetic
expansion. The name following the $ is expanded. The name may
be included in braces, mostly to protect it from following
expansions. Braces are required if the name is a positional
parameter with more than one digit.
DIR="x y z" |   |  
|
ls $DIR | = | ls x y z
|
  |   |  
|
CMD="ls" |   |  
|
USER="bob" |   |  
|
DIR="files" |   |  
|
assume ${13} | = | abc
|
$CMD /home/${USER}$DIR ${13} | = | ls /home/bob/files/abc
|
There are quite a group of special cases. In the following, if
parameter is set, it is used. If not word is used in the following
way:
${parameter:-word} - use word (Use default values)
ls ${DIR:-~} = | ls $DIR or ls ~
|
${parameter:=word} - assign word (Assign default values)
ls ${NAME:=Bob}= | ls ~ set NAME="Bob"
|
${parameter:?word} - write word to standard error (Display error)
ls ${DIR:="undefined"}= | ls $DIR or output "undefined"
|
${parameter:+word} - substitute word if parameter is set
otherwise substitute nothing (Substitute alternate value)
CCOPTS=${DEBUG:="-g"}= | CCOPTS="-g" or unset
|
These are quite usefult in scripts for setting default values and
handling undeclared parameters and/or environment variables. However,
they are not widely used.
command substitution
Replace a variable with the output of the command represented by
the variable. This the result of placing the command in
single left quotes. More on this later.
arithmetic expansion
word splitting substitution
Any result of parameter expansion, command
substitution or arithmetic expansion not in double quotes is
split into words using IFS (default = space,tab,newline) as
the delimiter.
pathname expansion
Quoting
Quoting is used to remove the special nature of some characters and
words. In addition, the escape character "\" is used to preserve the
literal value of a character that can be used literally in the shell, or
as a special character. To summarize, there are three types of quoting:
Double Quotes
All characters have their literal value preserved, with the exception of
$,` and \. $ and ` always retain their special
meaning, while \ retains it only when followed by $,`,",\
or newline. A double quote may occur between double quotes if
it is escaped.
Single Forward Quotes (left quotes)
Synonym for command substitution. The value of the expression is
the result of the command execution.
Single Backward Quotes
All characters have their literal value preserved. A single quote may
not occur between single quotes.
For example, assume CMD=ls
echo $CMD ~/xyz | ls /home/bozo/xyz
|
echo "$CMD ~/xyz" | ls ~/xyz
|
echo '$CMD ~/xyz' | $CMD ~xyz
|
echo `$CMD ~/xyz` | noses bigfeet baggypants
|
echo "\$CMD ~/xyz" | $CMD ~/xyz
|
echo '\$CMD ~/xyz' | \$CMD ~xyz
|
echo `$CMD ~/xyz` | noses bigfeet baggypants
|
echo `$CMD "~/xyz"` | No such file or direcory
|
echo "$CMD '~/xyz'" | ls '~/xyz'
|
echo "$CMD '~/xyz'" | ls '~/xyz'
|
echo "`$CMD` ~/xyz" | ls of current directory and execute ~/xyz
|
echo "`$CMD ~/xyz`" | noses bigfeet baggypants
|
echo '$CMD '~/xyz'' | ls $CMD ~/xyz
|
Builtin Commands
Bash or Shell has a long list of builtin commands, some of which you
already know. For example, pwd, chdir and so on. All of these commands
can be used in a shell script, but there are some others that are useful
primarily in programming situations.
Builtin Commands
Useful External Commands
There are, of course, hundreds of Unix/Linux commands that you can use,
but there are a few that are quite useful for shell programs.
External Commands
Programming Constructs
Any bash command can be used in a script, but this section will concentrate
on programming constructs of various kinds. In general, the format of
a script file is freeform, with newlines used to separate executable lines
of the script.
Simple Commands
Simple Examples
 
- Any Linux command.
- Any exported shell environment variable or alias
command[(arguments)]
execute the external or internal command
lists
(list)
list is executed in a subshell
(echo `wc *`)
{list;}
list executed in the current shell
Examples
for
for name [in word] do list
; done
The list of words following in is expanded creating a list
of items. name is set to each in sequence and list
is executed. If the in clause in not included, the
positional parameters are used by default.
Examples
select
select name [in word] do list
; done
The list of words following in is expanded creating a list
of items. The list is output to stderr and prompt PS3 is
output. A line is read from stdin and name is set to each
the corresponding value. Then list is executed. This
continues until a break or return is executed.
Examples
case
case word in [pattern [| pattern]...]
list;;]... esac
word is expanded and matched against each pattern. When a
match is found, the corresponding list is executed. The exit
status is zero if no match is found or the status of the last command
in list executed.
Examples
if-then-else
if list then list[elif list
then list] ... [else list fi
if list is executed and if the status is zero
then list is executed, otherwise elif's
are executed similarly in order. If none are executed, the
else list is executed.
command1 && command2
command2 is executed if and only if command1 returns an exit
status of zero. This is equivalent to:
if command1
then
command2
endif
command1 || command2
command2 is executed if and only if command1 returns an exit
status which is non-zero. This is equivalent to:
if ! command1
then
command2
endif
Examples
while,until
while list do list done
until list do list done
The do list is executed as long as the while
list returns a status of zero. The until is the
same except that is continues while the status in non-zero.
Examples
functions
[function] name () {list;}
Examples
read name1,name2,... | Read consecutive words into variables
name1, name2, etc.
| read | Read the input string into the variable REPLY
| read -r name1,name2 | read without intepreting backslashes
| |
| |
|
Assignment
- Create a shell script that will find all files in the user home
directories that have not been accessed in the last 90 days. This is
a great way to control disk space usage if you set it up as a cron job
to run once per week and then tell users to archive the files or
remove them.
- Create a script to find all directories in a subtree and change their
permissions to rwxr-xr-x (755) and all regular files to rw-r--r-- (644).
Use the find command with the type option and xargs to execute
chmod.
- Create a script that will build a file named
/var/local/security/filecheck/xxxxx
that contains file names, sizes, creation times and access times for
files. You should be able to specify a directory and have a file
created, where the "xxxxx" above becomes the name of the directory.
For example, /var/local/security/filecheck/usr-bin for /usr/bin.
A nice modification is to allow directories or individual files to be
handled this way.
- Using the cksum command, create checksums to be placed in the file
instead of the file size.
- Create a shell script that will read the file created in the previous
questions and then
check to insure that the current configuration matches what is
stored in the file. Obviously, the use of this script is to
make sure that critical files in your system haven't been
modified. Set up a script to run regular checks on /sbin, /bin,
/usr/bin and /usr/sbin.
- Here are some other things you could try if you wanted to:
- Create a script to check various system performance characteristics and
output it to a window on your system on a regular basis.
- Create a script to clean up a directory tree by removing files
that are typically unnecessary, such a executables, .ps files or
.o files. Note that this is usually a pretty personal set of
files.
- Write a script to test and determine if there is nefarious activity
on a TCP port (netstat will give you the data).
- Write a script to regularly collect network statitistics and send mail
to root if network performance is degrading.
- Do the same for CPU, memory or disk performance.
|