Previous Table of Contents Next

Module 108
Programming Constructs - Shell Programming


Shell programming constructs are used to build shell scripts. In this module we discuss the use of various shell programming constructs and the basics of creating executable shell scripts.

Before you name your shell script, verify that is does not already exist on the system. You can use the whence, type, or which command, depending on your login shell, to see if your current PATH can locate a command with the same name. If no command is found, then you can use the name you selected.

If you work in a heterogeneous environment, you will probably want to locate the shell with the most features common to all systems and program to that standard. A heterogeneous environment consists of several types of hardware and various versions of UNIX. A homogeneous environment consists of at least all the same versions of the operating systems. The hardware may or may not be different. So you can have heterogeneous or homogeneous hardware mixed with heterogeneous or homogeneous software.

If you write shell scripts that are used on all of the systems, you will want to consider the problem of porting and maintaining the scripts. Although you may not be able to use the latest and greatest shell features of one system, by using a shell common to all systems, you will make life easier for yourself and the next person who has to perform your job.

We only cover the constructs of the ksh in this module. The sh is a subset of the Korn shell. The csh shell is not commonly used for shell programming, thus it is not discussed. I am sure csh lovers will disagree! The constructs are listed next to the ksh constructs for those who believe they must use the csh to program with.

If the first character of a file is a # (number sign) the script is executed as a csh script. If it is not a # the script is executed using the /bin/sh shell. You can also specify a type of interpretor by using #!interpretor on the first line of the script file. Where interpretor might be /bin/sh, /bin/ksh, /bin/csh, /bin/awk, etc.


Following is the general format of a shell script command.

     shell_script_name [ options ] [ arguments ]

Since you create a shell script you select the name. The options and arguments depend on your implementation of the programming solution.


Since you create a shell script you decide what options are needed. When processing options within a shell script you should always use the getopts command when it is available. The getopts command is supported on System V Release 3 and later revisions.

BSD (Berkeley)
Most BSD systems DO NOT support the getopts command. Therefore, you have to process the options using a while or for loop.


Arguments should also be processed using the getopts command. Again you write the script, so you decide what arguments are valid.


Before you start creating local shell scripts you might consider making a directory to store your shell scripts. For example,

     mkdir bin
     chmod 755 bin

creates a directory named bin in your HOME directory. You will need to update your .profile to have the new directory in your PATH. For example, edit your $HOME/.profile, search for the last occurence of PATH (:g/PATH/p), and add the following line.

     PATH=$PATH:~/bin            # For Korn shell
     PATH=$PATH:$HOME/bin        # For Bourne shell

This adds your personal bin directory to your path. Now cd to bin to create new shell script commands.


The following steps outline the creation of a simple shell script.

Don't forget that the shell offers the ability to alias commands. Since aliases are much faster than shell scripts you might consider using aliases instead of one- or two-line shell scripts.

To create a shell script you simply enter the editor of your choice. The filename should be the name you want to use to invoke your shell script. For example, let's say you want a shell script to list all users in your group. You might want to call the command "lg" for list groups. To create the script invoke vi with lg as an argument.

     vi lg

Once in the editor, you write the necessary commands to perform the desired task. In this case the commands would look something like the following code.

     set `grep $LOGNAME /etc/passwd`
     awk -F: '$4 == "'${GID}'" { printf "%15s %5s\n", $1, $4 }' /etc/passwd

The OIFS variable is set to the current value of IFS (space, tab, new-line). Then IFS is reset to a colon (:). A colon is the field separator in the /etc/passwd file. Then you grep the user name from the /etc/passwd file. Perform a set command so the fields in the line you greped are set to positional parameters ($1, $2, ...). Then set the GID variable to $4, which is the group ID field. Now you run awk on the /etc/passwd file searching for lines where field four ($4) matches the saved group ID (GID). If a match occurs, the user name ($1) and group ID ($4) are printed.

After you have edited the text, exit the editor. Change the modes on the file to make it executable. For example,

     chmod 755 lg


     chmod +x lg

sets the mode to rwxr-xr-x. Now you and all other users can execute the command lg. But only you have write permission.


The Korn shell provides various commands to allow for structured programming. These include conditional, iteration, and grouping commands, plus the use of functions to allow for high level language type programming.


The Korn shell has a built-in command to handle arithmetic expressions. The command islet which is discussed in Module 73. The Bourne shell does not have a built-in arithmetic command; therefore, the expr command is used. It is discussed in Module 48.


(Program Flow Control)Conditional commands allow you to execute a list of commands based on the result of testing an expression.

if elif else

The if statement is used to check the return code of a command list. If the return code is zero (0) the commands inside the if statement are executed. If the return code is nonzero the elif or else part of the statement is performed.


The format of the if statement follows.

     if   command_list_1

   [ elif command_list_3
          command_list_4 ]
   [ else
          command_list_5 ]

C Shell
if (expr) then
else if (expr2) then

Notice that the elif and else statements are not required in the if construct.

Alternative Flow Control

The shell offers an alternative way to handle simple if then type statements. Instead of using if statements you use the conditional symbols || and &&. For example,

     if [ -r "$FILE" ]
     then pg $FILE

could be written as

     [ -r "$FILE" ] && pg $FILE

The && symbol executes the second command if the return code of the first command is zero. Conversely, the || symbol executes the second command only if the return code of the first command was nonzero. For example,

     [ -d "$FILE" ] || pg $FILE

executes the pg command if the value of $FILE is not a directory. So you could write

     [ -r "$FILE" ] && [ -d "$FILE" ] || pg $FILE

which executes the pg command if the value of FILE is readable and is not a directory. Another way to write this is:

     [ -r "$FILE" ] && [ -f "$FILE" ] && pg $FILE

This executes the pg command if the value of FILE is readable and is a file.


The case statement is used to perform multiple choice branches. The case statement is better suited to handle the comparison of strings than the if statement. But it is not suited for using the return codes of commands, such as the test command.


The format of the case statement follows.

     case word in
     pattern) command_list ;;

C Shell
switch (word)
  case pattern:
  default: breaksw

The case statement takes the value of word and compares it to the list of patterns. The word may be a variable, output from a command substitution, or a constant. The command_list following the first pattern that matches word is executed. If no match is made, then the case statement is passed over as if it does not exist. It is advisable to enclose word in double quotes. This prevents the shell from aborting if word is nonexistent or NULL.

The case is well-suited for processing options within a shell script. For example, the following code can be used to process the -ffile or -f file option and option argument.

     while [ -n "$*" ]
       case "$1" in
        -f) shift; FILE=$1 ;;
       -f*) FILE=`expr $1 : '..\(.*\)' ;;

In the case statement if the -f option is separate from the filename, it is shifted out of the way and the filename is assigned to the FILE variable. If the filename is next to the -f option, the case matches it with the -f* and executes the expr command to strip off the -f part before assigning the filename to FILE.

In the Korn shell you can replace the expr command with the let command and with variable substitution commands. To perform the previous expr command you would use


The #-f removes the -f from the beginning of the $1 variable. This is much faster and easier to remember than the old expr command.


Iteration commands allow you to execute a list of commands while an expression remains true. When the expression returns a false value the loop is terminated.


The for loop executes a specific number of times based on the number of words in its word list. If no word list is given, the positional parameters are processed. It provides a simple way to step through lists of information.


The format of the for loop follows.

  for NAME in wordlist

C Shell
foreach NAME (wordlist)

The for loop provides a simple means of processing lists of information. It executes a set of commands for each word in the list. Once the list of words is exhausted the loop automatically ends.

For example, the following code illustrates a for loop processing the output of an ls command. This shell script interactively moves files from a specified directory to another directory. You must specify the directory you want to move files from and the destination directory. The source directory is $1 and the destination directory is $2.

Previous Table of Contents Next