next up previous contents
Next: Trap in Bourne shell Up: Bourne shell Previous: Bourne shell

Bourne shell programming

Bourne shell acts on string variables of 3 types: user, shell, read-only shell. Variables are assigned values with the \framebox{=}= operator and referenced by value when prefixed by $. To assign values with embedded blanks, quote or double quote the string. A variable can be declared readonly using the builtin readonly, but then is cannot be reused as read-write. A read-write variable can be removed by unset. Variables are protected with braces as shown in the example below:

> cat cksh
a='This is a'
echo "${a}n example"
echo "$a test"
> cksh
This is an example
This is a test


Shell variable evaluation rules (0),(1)
var= set variable to NULL
$var value of var
${var} value of var
${var1-var2} value of var1 or var2 if var1 undef, or command `var2`
${var=var1} set var = var1 if var undef
${var?message} print message if var undef
${var1+var2} value of var2 if var1 defined
(0) - No space allowed in brace expresssions.
(1) - The operators may be prefixed by : to perform the same action, but only when
the variable is set and not null


Scripts can handle arguments. Arguments are passed throught the positional parameters $1..$9, while $0 is the name of the script (command) itself. If you need more than 9 arguments, you can store in local variables the first argument and use shift that shifts all arguments by 1 place. Positional parameters are global and can be passed to nested scripts. Parameters may be assigned as keywords with the following syntax:

par1=key1  par2=key2  key_command
Note that keywords are not counted by $# and are recognized only if the command is run from a Bourne shell:

$ sh
$ cat cksh
#!/bin/sh
echo $a $b
$ a=1 b=2 cksh
1 2
$ a=john b=mary cksh
john mary
If cksh is run from another shell like tcsh, the following error message is typed:
> sh a=john b=mary cksh
sh: a=john: cannot open
To use cksh from another shell, it must be called by the following script:
> cat sh0
#!/bin/sh
sh &
a=john b=mary cksh
Note that sh must be called in background ( sh & statement)

Variables are made global to the sub-shells if declared in the export argument list. Exported variables are queried by export without arguments. Exported variable values are typed by env. Any single exported variable value is given by printenv var.

As in most shells, default field separators are <space> and <tab>. Other separators may be defined using IFS:

$ IFS=":"; export IFS   # add : to field separators
Shell symbols are:


$1..$9 		  arguments

$0 process name
$# no. of arguments at call
$? exit status (0 = true = success, .not.0 = false = failure)
$$ PID of this shell
$! PID of last background command
$- option supplied at shell call
$* full call args string
$@ call args with quotes
A script can be made executable in 5 different ways:


script_name run file script_name if +x permission set on file
sh script_name fork new shell to run script_name ( -x)
. script_name run script_name as part of current process ( -x)
exec script_name run script_name in place of current process,
  do not go back to original program
`script_name` replace script_name with script_name output


A simple Bourne shell script example is the following:

> cat shodef
#!/bin/sh
#------------shodef: simple script to show date & dir---------------
#
clear
echo "current date & time: \c"
date
echo "user `whoami` at dir: \c"
pwd
echo
#--------end script-----------

shodef clears the display ( clear), writes a comment suppressing new line (\ c), shows the current time and date, writes a comment including user name by executing ` whoami` (note the backquote that forces execution of the embedded command) and prints current directory on the same line, as shown below.

current date & time: Wed Jul  1 14:49:27 MET DST 1998
user jones at dir: /user/jones/dev
Backquotes can be nested in the form \`:

$ jj=`echo date is`
$ echo $jj
date is
$ jj=`echo date is: \`date\``
$ echo $jj
date is: Tue Sep 8 15:41:01 MET DST 1998
Another example of command substitution in cooperation with set:

> cat shodate
#!/bin/sh
#-----------shodate: example of set and `com`---------------
#
set `date`           # insert date output in args
echo ""
echo "date command args are $# with values:"
#
echo ""
for args
  do
    echo $args       # print all date items
  done
#
echo ""
#------------end proc--------------------

The above example inserts in the arg list the item returned by date using set and the indirect command `date`, then the loop over arguments for args prints all arguments extracted from date.

> shodate
date command args are 7 with values:
Mon
Jul
12
09:18:37
MET
DST
1999
>

To inquire script items not typed as arguments on the command line Bourne shell allows input from the keyboard with the read statement. Bourne shells supports also the complete if structure with the following syntax:

if ...
then ...
elif ... then
else
fi

A simple example of read and if is given below. Note also the redirection to the empty device /dev/null of the grep operation. In this way only grep status of is checked and grep output is discarded.

#!/bin/sh
#-----------inp: read input from tty
#
echo Proc $0: a demo of vt input
echo
echo "Please enter a name: \c"
read name
#
#----check if $name logged-------
#
if who | grep -s $name > /dev/null
then
  echo $name is logged
else
  echo no such user $name
fi
#----------end script------------------
> inp
Please enter a name: mary
mary is logged
> inp
Please enter a name: john
no such user john
>
Conditional execution is improved by the test statement. To check whether $file esists do:

#----check if $file exists-------
echo "Please enter a file name: \c"
read file
if test ! -f $file
then
  echo no such file: $file
else
   ls -al $file
fi
#----------end script------------------
> exists
Proc exists: A demo of the test command
Please enter a file name: any
no such file any
> exists
Please enter a file name: loop
-rwxr-xr-x   1 james    users        277 Jul  2  1998 loop
>

The test command evaluates an expression constructed of functions and operators. If the value of expression is true, test returns an exit value of zero (0); otherwise, it returns FALSE, a nonzero exit value. The test command also returns a nonzero exit value if there are no arguments.


test command syntax
-r file TRUE if file exists and has read permission
-w file TRUE if file exists and has write permission
-x file TRUE if file exists and has execute permission
-f file TRUE if file exists and is a regular file
-d file TRUE if file exists and is a directory
-e file TRUE if file exists
-c file TRUE if file exists and is a character-special file
-b file TRUE if file exists and is a block-special file
-p file TRUE if file exists and is a named pipe ( FIFO)
-h file TRUE if file exists and is a soft link (same as -L [Digital])
-L file TRUE if file exists and is a soft link (same as -h)
-u file TRUE if file exists and its set-user ID bit is set
-g file TRUE if file exists and its set-group ID bit is set
-k file TRUE if file exists and its sticky bit is set. [ Digital]
-s file TRUE if file exists and has a size greater than zero ( 0)
-t descriptor TRUE if the open file with file descriptor number descriptor
  ( 1 by default) is associated with a terminal device
-z string1 TRUE if the length of string1 is zero ( 0)
-n string1 TRUE if the length of string1 is nonzero
str1 = str2 TRUE if str1 and str2 are identical
str1 != str2 TRUE if str1 and str2 are not identical
string1 TRUE if string1 is not the null string
num1 -eq num2 TRUE if the integers num1 and num2 are algebraically equal.
  Any of the comparisons -ne, -gt, -ge, -lt, and -le
  can be used in place of -eq
The listed functions can be combined with the following operators:
! Unary negation operator
-a Binary AND operator
-o Binary OR operator, -a operator has higher precedence
  than -o operator
EXIT VALUES
0 The test command evaluated expression is TRUE
1 The test command evaluated expression is FALSE,
  or there are no arguments
> 1 An error occurred


Examples:

1.
To test whether a file exists and is not-empty, enter:
        if test ! -s "$1"
        then
          echo $1 does not exist or is empty.
        fi
The double quotes around $1 ensure that the test will work properly even if the value of $1 is the empty string. If the double quotes are omitted and $1 is the empty string, test displays the error message test: parameter expected. Note that quotes and double quotes are treated as in Korn shell.

2.
To do a complex comparison, enter:
        if [ $# -lt 2  -o  ! -s "$1" ]
        then
          exit
        fi
If the shell procedure was given fewer than 2 positional parameters or the file specified by $1 does not exist or is empty, then exit.

3.
An example of test in a if/elif/else structure:

echo "\n \n Proc $0: A demo  of the "elif" structure "
if test $# = 0
then
  echo "                           supply file name !!!"
elif test ! -f $1
then
  echo "                           no such file $1"
else
   dir $1
fi
Bourne shell supports the case statement:
case item in
pattern1) command
;;
pattern2) command
;;
esac
Example:
#!/bin/sh
#-----------excase: example of case statement
#
today=`date +%d/%m`                  #n.b. meaningful blanks 
case $today in
     16/07) echo "Thu 16 July"
     ;;
     06/07) echo "Monday"
     ;;
     10/07) echo "Meeting"
     ;;
     *) echo "none"
esac
#
#----------end script------------------
In Bourne shell loops are implemented with the for, while, until statements:
for var in list
do
commands
done

while list # repeat commands if exit status == 0
do
commands
done

until list # repeat commands if exit status != 0
do
commands
done

To stop a loop use break, to skip a loop step use continue. See the below example in which eval is used to execute the command.
#!/bin/sh
#-----------loop: example of while statement
#
while echo "Enter a command or end"
  read cmd
  do
    case $cmd in
       'end') break     # end proc
       ;;
       "") continue     # ignore <ret>
       ;;
       *) eval $cmd     # do command
       ;;
     esac
  done
#
#----------end script------------------
A conditional exit is executed by exit n where n is an integer giving the status.

Other less frequently used Bourne shell statements are:


(list) 		 execute commands in a subshell     
{ list; } execute commnads in the current shell
name () { list; } define a function referenced by name

Bourne shell can be run as a login shell set in /etc/passwd/ or as a subshell. Bourne shell is run by the sh command that is called in the form -sh when started as a login shell. In this case sh executes .profile and $HOME/.profile first. sh call flags are:


-c str 		 run commands from  str

-i make the shell interactive
-r create a restricted shell
-s read commands from stdin


Bourne Shell Redirection Options
<file use file as stdin
>file use file as stdout
>>file append to file
<<[-]EOF_str read till a line containing only EOF-str is met
  the dashed form <<- strips leading <tabs>
< &n associate stdin with file descriptor n
> &n associate stdout with file descriptor n
< &- close stdin
> &- close stdout



Bourne Shell set Synopsis
Syntax: set [ + ][ -flag] [argument ...]
To customize environment, use - to set flag and + to unset
-a mark for export all modified variables
-e immediate exit on nonzero exit status
-f disable file name substitution
-h locate command called in function at function definition
  (default is at function execution)
-k place all keyword parameters in the command environment
-n read but do not execute commands
-t exit after command execution
-u treat unset variable as error
-v trace shell input lines (debug mode)
-x display command & arguments at execution (debug)
- - do not change flags, useful for $1 starting with dash



Bourne Shell Built-in Commands
: null command
. file execute command in current shell
break [n] exit from loop
continue [n] skip loop step
cd change directory
echo write arguments to stdout
eval read arguments and execute commands
  Ex. eval `tset -s vt100` sets term type
exec execute command in place of current shell
  without creating a new process
exit [n] exit and optionally set exit status to n = 0:255
hash [-] [name ...] rehash command table
inlib obsolete command, use loader instead
newgrp change group (see also man page)
pwd print current directory
read assign var from stdin
readonly mark var as readonly
return [n] exit function with optional return status n
rmlib obsolete command, use loader instead
set [+| -flag] [arg] set flags 1
shift [n] shift left argument list
test evaluate conditional expressions 2
times dispaly used time
trap trap signals
type indicate how the shell interprets the command
ulimit display or adjust shell resources 3
umask set default file protection
unset remove variable
wait [n] wait child process n completion
(1) - see set synopsis
(2) - see test syntax table
(3) - see ulimit synopsis



Bourne Shell ulimit Synopsis 0
Syntax: ulimit [-HS] -[a|c|d|f|h|m|n|d|t] [limit]
default flag value: -f
-a set/display address space for the shell
-c set/display core segment size for the shell
-d set/display data segment size for the shell
-f set/display file size for the shell
-h set/display current hard resource
-H set/display hard resource limit (superuser)
-m set/display memory allocation for the shell
-n set/display maximum open file descriptors for the shell
-s set/display stack segment size for the shell
-S set/display soft resource limit
-t set/display CPU maximum time for the shell
(0) - for more information on resource setting see getrlimit



next up previous contents
Next: Trap in Bourne shell Up: Bourne shell Previous: Bourne shell
Marisa Luvisetto
2001-02-05