Chapter 10. The Korn Shell

CONTENTS
  •  10.1 Interactive Korn Shell
  •  10.2 Programming with the Korn Shell
  •  KORN SHELL LAB EXERCISES

graphics/ch10.gif

10.1 Interactive Korn Shell

Before the Korn shell displays a prompt, it is preceded by a number of processes. See Figure 10.1.

Figure 10.1. System startup and the Korn shell.

graphics/10fig01.gif

10.1.1 Startup

The first process to run is called init, PID 1. It gets instructions from a file called inittab (System V) or spawns a getty (BSD) process. These processes open up the terminal ports, provide a place where input comes from, stdin, and the place where standard output (stdout) and standard error (stderr) go, and put a login prompt on your screen. The /bin/login program is then executed. The login program prompts you for a password, encrypts and verifies the password, sets up an initial environment, and starts up the login shell, /bin/ksh, the last entry in the passwd file. The ksh program looks for the system file, /etc/profile, and executes its commands. It then looks in the user's home directory for an initialization file called .profile, and an environment file, conventionally called .kshrc. After executing commands from those files, the dollar sign prompt appears on your screen and the Korn shell awaits commands.

10.1.2 The Environment

The Initialization Files. After executing the commands in /etc/profile, the initialization files in the user's home directory are executed. The .profile is executed, followed by the ENV file, commonly called the .kshrc file.

The /etc/profile File. The /etc/profile is a systemwide readable file set up by the system administrator to perform tasks when the user logs on and the Korn shell starts up. It is available to all Bourne and Korn shell users on the system, and normally performs such tasks as checking the mail spooler for new mail and displaying the message of the day from the /etc/motd file. The following text is an example of the /etc/profile. See Chapter 8, "The Interactive Bourne Shell," for a complete explanation of each line of /etc/profile.

EXAMPLE # The profile that all logins get before using their own .profile     trap " " 2 3     export LOGNAME PATH  # Initially set by /bin/login     if [ "$TERM" = " " ]     then        if /bin/i386        then     # Set the terminal type           TERM=AT386        else           TERM=sun        fi        export TERM     fi     # Login and -su shells get /etc/profile services.     # -rsh is given its environment in the .profile.     case "$0" in     -sh | -ksh | -jsh )         if [ ! -f .hushlogin ]         then           /usr/sbin/quota        # Allow the user to break the Message-Of-The-Day only.           trap "trap ' ' 2" 2           /bin/cat -s /etc/motd           # Display the message of the day           trap " " 2           /bin/mail -E           case $? in           0)        # Check for new mail               echo "You have new mail. "                    ;;           2)  echo "You have mail. "                    ;;           esac        fi     esac     umask 022     trap 2 3 

The .profile File. The .profile file is a user-defined initialization file, that is executed once at login (by the Bourne and Korn shells) and is found in your home directory. It gives you the ability to customize and modify your working environment. Environment variables and terminal settings are normally set here, and if a window application or dbm is to be initiated, it is started here. If the .profile file contains a special variable called ENV, the filename that is assigned to that variable will be executed next. The ENV file is often named .kshrc; it contains aliases and set o commands. The ENV file is executed every time a ksh subshell is spawned. The lines from the following files may not be meaningful to you now, but all of the concepts, such as exporting variables, history, the search path, and so on, will be discussed in detail throughout the text of this book.

Example 10.1
1   set -o allexport 2   TERM=vt102 3   HOSTNAME=$(uname -n) 4   HISTSIZE=50 5   EDITOR=/usr/ucb/vi 6   ENV=$HOME/.kshrc 7   PATH=$HOME/bin:/usr/ucb:/usr/bin:\     /usr/local:/etc:/bin:/usr/bin:/usr/local\     /bin:/usr/hosts:/usr/5bin:/usr/etc:/usr/bin:. 8   PS1="$HOSTNAME ! $ " 9   set +o allexport 10  alias openwin=/usr/openwin/bin/openwin 11  trap '$HOME/.logout' EXIT 12  clear 

EXPLANATION

  1. By setting the allexport option, all variables created will automatically be exported (made available to subshells).

  2. The terminal is set to vt102.

  3. The variable HOSTNAME is assigned the name of this machine, $(uname n).

  4. The HISTSIZE variable is assigned 50; 50 lines from the history file will be displayed on the terminal when the user types history.

  5. The EDITOR variable is assigned the pathname for the vi editor. Programs such as mail allow you to select an editor in which to work.

  6. The ENV variable is assigned the path to the home directory ($HOME) and the name of the file that contains further Korn shell customization settings. After the .profile is executed, the ENV file is executed. The name of the ENV file is your choice; it is commonly called .kshrc.

  7. The search path is defined. It is a colon-separated list of directories used by the shell in its search for commands typed at the prompt or in a script file. The shell searches each element of the path from left to right for the command. The dot at the end represents the current working directory. If the command cannot be found in any of the listed directories, the shell will look in the current directory.

  8. The primary prompt, by default a dollar sign ($), is set to the name of the host machine, the number of the current command in the history file, and a dollar sign ($).

  9. The allexport option is turned off.

  10. An alias is a nickname for a command. The alias for openwin is assigned the full pathname of the openwin command, which starts Sun's window application.

  11. The trap command will execute the .logout file when you exit this shell, that is, when you log out. The .logout file is a user-defined file containing commands that are executed at the time of logging out. For example, you may want to record the time you log out, clean up a temporary file, or simply say So long.

  12. The clear command clears the screen.

The ENV File. The ENV variable is assigned the name of a file that will be executed every time an interactive ksh or ksh program (script) is started. The ENV variable is set in the .profile and is assigned the name of the file that will contain special ksh variables and aliases. The name is conventionally .kshrc, but you can call it anything you want. (The ENV file is not processed when the privileged option is on. See Table 10.1.)

Example 10.2
1   set -o trackall 2   set -o vi 3   alias l='ls -laF'     alias ls='ls -aF'     alias hi='fc -l'     alias c=clear 4   function pushd { pwd > $HOME/.lastdir.$$ ; }     function popd { cd $(< $HOME/.lastdir.$$) ;            rm $HOME/.lastdir.$$; pwd; }     function psg { ps -ef | egrep $1 | egrep -v egrep; }     function vg { vgrind -s11 -t $* | lpr -t ; } 

EXPLANATION

  1. The set option for tracked aliases is turned on. (For a complete description, see "Aliases".)

  2. The set option for the vi editor is turned on for in-line editing of the history file. (See "Command Line History".)

  3. The aliases (nicknames) for the commands are defined.

  4. The functions are named and defined. (See "Functions".)

The set o Options. The set command can take options if the o switch is used. Options allow you to customize the shell environment. They are either on or off, and are normally set in the ENV file.

FORMAT

set -o option  Turns on the option. set +o option  Turns off the option set -[a-z] Abbreviation for an option; the minus turns it on set +[a-z] Abbreviation for an option; the plus turns it off 
Example 10.3
set -o allexport set +o allexport set -a set +a 

EXPLANATION

  1. Sets the allexport option. This option causes all variables to be automatically exported to subshells.

  2. Unsets the allexport option. All variables will now be local to the current shell.

  3. Sets the allexport option. Same as 1. Not every option has an abbreviation (see Table 10.1).

  4. Unsets the allexport option. Same as 2.

 

Table 10.1. Options
Name of Option Abbreviation What It Does
allexport a Causes variables set to be automatically exported.
bgnice   Background jobs are run with a lower priority.
emacs   For command line editing, uses the emacs built-in editor.
errexit e If a command returns a nonzero exit status (fails), executes the ERR trap, if set, and exits. Not set when reading initialization files.
gmacs   For command editing, uses the gmacs built-in editor.
ignoreeof   Prevents logout with ^D; must type exit to exit the shell.
markdirs   Puts a trailing backslash (/) on directory names when filename expansion is used.
monitor m Allows job control.
noclobber   Protects files from being overwritten when redirection is used.
noexec n Reads commands, but does not execute them. Used to check the syntax of scripts. Not on when running interactively.
noglob f Disables pathname expansion; i.e., turns off wildcards.
nolog   Does not store function definitions in the history file.
notify   Notifies user when background job finishes (only in versions newer than 1988).
nounset   Displays an error when expanding a variable that has not been set.
privileged p When set, the shell does not read the .profile or ENV file; used with setuid scripts.
trackall   Enables alias tracking.
verbose v Turns on the verbose mode for debugging.
vi   For command line editing, uses the vi built-in editor.
xtrace x Turns on the echo mode for debugging.

10.1.3 The Prompts

The Korn shell provides four prompts.The primary and secondary prompts are used when the Korn shell is running interactively. You can change these prompts. The variable PS1 is the primary prompt, set initially to a dollar sign ($). It appears when you log on and waits for you to type commands. The variable PS2 is the secondary prompt, initially set to the > character. It appears if you have partially typed a command and then pressed Enter. You can change the primary and secondary prompts.

The Primary Prompt. $ is the default primary prompt. You can change your prompt. Normally, prompts are defined in .profile.

Example 10.4
1   $ PS1="$(uname  n) ! $ " 2   jody 1141 $ 

EXPLANATION

  1. The default primary prompt is a $. The PS1 prompt is being reset to the name of the machine $(uname n), the number of the current history number, and the $. The exclamation point evaluates to the current history number. (To print an exclamation point, type two exclamation points (!!) in the PS1 definition.)

  2. The new prompt is displayed.

The Secondary Prompt. The PS2 prompt is the secondary prompt. Its value is displayed to standard error (the screen). This prompt appears when you have not completed a command and have pressed the carriage return.

Example 10.5
1   $ print "Hello 2   > there" 3   Hello     there 4   $ 5   $ PS2=" > " 6   $ print "Hi 7    >      >      > there"     Hi     there     $ 

EXPLANATION

  1. The double quotes must be matched after the string "Hello.

  2. When a newline is entered, the secondary prompt appears. Until the closing double quotes are entered, the secondary prompt will be displayed.

  3. The output of the print command is displayed.

  4. The primary prompt is displayed.

  5. The secondary prompt is reset.

  6. The double quotes must be matched after the string "Hi.

  7. When a newline is entered, the new secondary prompt appears. Until the closing double quotes are entered, the secondary prompt will be displayed.

10.1.4 The Search Path

To execute a command typed at the command line or within a shell script, the Korn shell searches the directories listed in the PATH variable. The PATH is a colon-separated list of directories, searched by the shell from left to right. The dot in the PATH represents the current working directory. If the command is not found in any of the directories listed in the path, the shell sends the message ksh: filename: not found to standard error. It is recommended that the path be set in the .profile file. To speed up the searching process, the Korn shell has implemented tracked aliases. See "Tracked Aliases" on page 455.

Example 10.6
$ echo $PATH /home/gsa12/bin:/usr/ucb:/usr/bin:/usr/local/bin:. 

EXPLANATION

The Korn shell will search for commands starting in /home/gsa12/bin. If the command is not found there, /usr/ucb is searched, then /usr/bin, /usr/local/bin, and finally the user's home directory represented by the period.

The Dot Command. The dot command (.) is a built-in Korn shell command. It takes a scriptname as an argument. The script will be executed in the environment of the current shell. A child process will not be started. The dot command is normally used to reexecute the .profile file or the ENV file, if either of those files has been modified. For example, if one of the settings in either file has been changed after you have logged on, you can use the dot command to reexecute the initialization files without logging out and then logging back in.

Example 10.7
$ . .profile $ . .kshrc $ . $ENV 

EXPLANATION

Normally a child process is started when commands are executed. The dot command executes each of the initialization files, .profile, the ENV file (.kshrc), in the current shell. Local and global variables in these files are defined within this shell. Otherwise, the user would have to log out and log back in to cause these files to be executed for the login shell. The dot command makes that unnecessary.

10.1.5 The Command Line

After logging in, the Korn shell displays its primary prompt, a dollar sign by default. The shell is your command interpreter. When the shell is running interactively, it reads commands from the terminal and breaks the command line into words. A command line consists of one or more words (or tokens), separated by whitespace (blanks and/or tabs), and terminated with a newline, generated by pressing Enter. The first word is the command, and subsequent words are the command's arguments. The command may be a UNIX executable program such as ls or pwd, a built-in command such as cd or jobs, or a shell script. The command may contain special characters, called metacharacters, which the shell must interpret while parsing the command line. If a command line is too long, the backslash character, followed by a newline, will allow you to continue typing on the next line. The secondary prompt will appear until the command line is terminated.

The Order of Processing Commands. The first word on the command line is the command to be executed. The command may be a keyword, a special built-in command or utility, a function, a script, or an executable program. The command is executed according to its type in the following order:[1]

  1. Keyword (such as if, while, until).

  2. Aliases (see typeset f).

  3. Built-in commands.

  4. Functions.

  5. Scripts and executables.

The special built-in commands and functions are defined within the shell, and therefore are executed from within the current shell, making them much faster in execution. Scripts and executable programs such as ls and pwd are stored on disk, and the shell must locate them within the directory hierarchy by searching the PATH environment variable; the shell then forks a new shell that executes the script. To find out the type of command you are using, use the built-in command, whence v , or its alias, type. (See Example 10.8.)

Example 10.8
$ type print print is a shell builtin $ type test test is a shell builtin $ type ls ls is a tracked alias for /usr/bin/ls $ type type type is an exported alias for whence -v $ type bc bc is /usr/bin/bc $ type if if is a keyword 

The Exit Status. After a command or program terminates, it returns an exit status to the parent process. The exit status is a number between 0 and 255. By convention, when a program exits, if the status returned is zero, the command was successful in its execution. When the exit status is nonzero, the command failed in some way. The Korn shell status variable ? is set to the value of the exit status of the last command that was executed. Success or failure of a program is determined by the programmer who wrote the program. In shell scripts, you can explicitly control the exit status by using the exit command.

Example 10.9
1   $ grep "ellie" /etc/passwd     ellie:GgMyBsSJavd16s:9496:40:Ellie Quigley:/home/jody/ellie 2   $ echo $?     0 3   $ grep "nicky" /etc/passwd 4   $ echo $?     1 5   $ grep "scott" /etc/passsswd     grep: /etc/passsswd: No such file or directory 6   $ echo $?     2 

EXPLANATION

  1. The grep program searches for the pattern ellie in the /etc/passwd file and is successful. The line from /etc/passwd is displayed.

  2. The ? variable is set to the exit value of the grep command. Zero indicates success.

  3. The grep program cannot find user nicky in the /etc/passwd file.

  4. If the grep program cannot find the pattern, it returns an exit status of one.

  5. The grep fails because the file /etc/passsswd cannot be opened.

  6. If grep cannot find the file, it returns an exit status of two.

Multiple Commands and Command Grouping. A command line can consist of multiple commands. Each command is separated by a semicolon, and the command line is terminated with a newline.

Example 10.10
$ ls; pwd; date 

EXPLANATION

The commands are executed from left to right until the newline is reached. Commands may also be grouped so that all of the output is either piped to another command or redirected to a file.

Example 10.11
$ ( ls ; pwd; date ) > outputfile 

EXPLANATION

The output of each of the commands is sent to the file called outputfile.

Conditional Execution of Commands. With conditional execution, two command strings are separated by two special metacharacters, && and ||. The command on the right of either of these metacharacters will or will not be executed based on the exit condition of the command on the left.

Example 10.12
$ cc prgm1.c  o prgm1 && prgm1 

EXPLANATION

If the first command is successful (has a zero exit status), the command after the && is executed.

Example 10.13
$ cc prog.c >& err || mail bob < err 

EXPLANATION

If the first command fails (has a nonzero exit status), the command after the || is executed.

Commands in the Background. Normally, when you execute a command, it runs in the foreground, and the prompt does not reappear until the command has completed execution. It is not always convenient to wait for the command to complete. By placing an ampersand (&) at the end of the command line, the shell will return the shell prompt immediately and execute the command in the background concurrently. You do not have to wait to start up another command. The output from a background job will be sent to the screen as it processes. Therefore, if you intend to run a command in the background, the output of that command should be redirected to a file or piped to another device such as a printer so that the output does not interfere with what you are doing.

Example 10.14
1   $ man ksh | lp& 2   [1]     1557 3   $ 

EXPLANATION

  1. The output of the manual pages for the Korn shell is piped to the printer. The ampersand at the end of the command line puts the job in the background.

  2. Two numbers appear on the screen: the number in square brackets indicates that this is the first job to be placed in the background; the second number is the PID, the process identification number, of this job.

  3. The Korn shell prompt appears immediately. While your program is running in the background, the shell is waiting for another command in the foreground.

10.1.6 Command Line History

The history mechanism keeps a numbered record of the commands that you have typed at the command line in a history file. You can recall a command from the history file and reexecute it without retyping the command. The history built-in command displays the history list. The default name for the history file is .sh_history, and it is located in your home directory.

The HISTSIZE variable, accessed when ksh first accesses the history file, specifies how many commands can be accessed from the history file. The default size is 128. The HISTFILE variable specifies the name of the command history file (~/.sh_history is the default) where commands are stored. The history file grows from one login session to the next; it becomes very large unless you clean it out. The history command is a preset alias for the fc l command.

The history Command/Redisplay Commands. The built-in history command lists previously typed commands preceded by a number. The command can take arguments to control the display.

Example 10.15
1   $ history            # Same as fc  l     1 ls     2 vi file1     3 df     4 ps  eaf     5 history     6 more /etc/passwd     7 cd     8 echo $USER     9 set     10 history 2   $ history  n         # Print without line numbers     ls     vi file1     df     ps  eaf     history     more /etc/passwd     cd     echo $USER     set     history     history  n 3   $ history 8          # List from 8th command to present     8   echo $USER     9   set     10  history     11  history  n     12  history 8 4   $ history  3         # List this command and the 3 preceding it     10  history     11  history  n     12  history 8     13  history  3 5   $ history  1  5      # List last 5 commands, preceding this one     13  history  3       # in reversed order.     12  history 8     11  history  n     10  history     9   set 6   $ history  5  1      # Print last 5 commands, preceding this one     10  history          # in order.     11  history -n     12  history 8     13  history -3     14  history  1  5 7   $ history            # (Different history list)     78  date     79  ls     80  who     81  echo hi     82  history 8   $ history ls echo     # Display from most recent ls command to     79  ls                # most recent echo command.     80  who     81  echo hi 9   $ history  r ls echo      #  r reverses the list     81  echo hi     80  who     79  ls 

Reexecuting Commands with the r Command. The r command redoes the last command typed at the command line. If the r command is followed by a space and a number, the command at that number is reexecuted. If the r command is followed by a space and a letter, the last command that began with that letter is executed. Without any arguments, the r command reexecutes the most previous command on the history list.

Example 10.16
1   $ r date     date     Mon Feb 15 12:27:35 PST 2001 2   $ r 3   redo command number 3     df     Filesystem   kbytes     used      avail     capacity    Mounted on     /dev/sd0a    7735       6282      680       90%         /     /dev/sd0g    144613     131183    0         101%        /usr     /dev/sd2c    388998     211395    138704    60%         /home. 3   $ r vi         # Redo the last command that began with pattern vi. 4   $ r vi file1=file2    # Redo last command that began with vi                           # and substitute the first occurrence of                           # file1 with file2. 

EXPLANATION

  1. The last command, date, is reexecuted.

  2. The third command in the history file is executed.

  3. The last command, starting with the string vi, is executed.

  4. The string file1 is replaced with the string file2. The last command, vi file1, is replaced with vi file2.

Command Line Editing. The Korn shell provides two built-in editors, emacs and vi, that allow you to interactively edit your history list. To enable the vi editor, add the set command listed below and put this line in your .profile file. The emacs built-in editor works on lines in the history file one line at a time, whereas the vi built-in editor works on commands consisting of more than one line. To set vi, type

set  o vi   or VISUAL=vi   or EDITOR=/usr/bin/vi 

If using emacs, type

set  o emacs    or VISUAL=emacs    or EDITOR=/usr/bin/emacs 

Note that set o vi overrides VISUAL, and VISUAL overrides EDITOR.

The vi Built-In Editor. To edit the history list, press the Esc key and use the standard keys that you would use in vi for moving up and down, left and right, deleting, inserting, and changing text. See Table 10.2. After making the edit, press the Enter key. The command will be executed and added to the bottom of the history list. To scroll upward in the history file, press the Esc key and then the K key.

Table 10.2. vi Commands
Command Function
Moving Through the History File
Esc k or + Move up the history list.
Esc j or - Move down the history list.
G Move to first line in history file.
5G Move to fifth command in history file for string.
/string Search upward through history file.
? String search downward through history file.
Moving Around on a Line
h Move left on a line.
l Move right on a line.
b Move backward a word.
e or w Move forward a word.
^ or 0 Move to beginning of first character on the line.
$ Move to end of line.
Editing with vi
a A Append text.
i I Insert text.
Editing with vi
dd dw x Delete text into a buffer (line, word, or character).
cc C Change text.
u U Undo.
yy Y Yank (copy a line into buffer).
p P Put yanked or deleted line down below or above the line.
r R Replace a letter or any amount of text on a line.

The emacs Built-In Editor. To start moving backward through the history file, press ^P. To move forward, press ^N . Use emacs editing commands to change or correct text, then press Enter and the command will be reexecuted. See Table 10.3.

Table 10.3. emacs Commands
Command Function
Moving Through the History File
Ctrl-P Move up history file.
Ctrl-N Move down history file.
Ctrl-B Move backward one character.
Ctrl-R Search backwards for string.
Esc B Move back one word.
Ctrl-F Move forward one character.
Esc F Move forward one word.
Ctrl-A Move to the beginning of the line.
Ctrl-E Move to the end of the line.
Esc < Move to the first line of the history file.
Esc > Move to the last line of the history file.
Editing with emacs
Ctrl-U Delete the line.
Ctrl-Y Put the line back.
Ctrl-K Delete from cursor to the end line.
Ctrl-D Delete a letter.
Esc D Delete one word forward.
Esc H Delete one word backward.
Esc space Set a mark at cursor position.
Ctrl-X Ctrl-X Exchange cursor and mark.
Ctrl-P Ctrl-Y Push region from cursor to mark into a buffer (Ctrl-P) and put it down (Ctrl-Y).

FCEDIT[2] and Editing Commands. The fc command is a built-in command that can be used with the FCEDIT variable (typically set in the .profile file) to invoke the editor of your choice for editing the history file. This can be any editor on your system. The FCEDIT variable is set to the full pathname of your favorite editor. If FCEDIT is not set, the default editor, /bin/ed, is invoked when you type the fc command.

The FCEDIT variable should be set to the chosen editor. You can specify a number of items from the history list that you want to edit. After you edit the commands, the Korn shell will execute the whole file. Any commands that are preceded by a pound sign (#) will be treated as comments and will not be executed. See Table 10.4 for more on commenting and filename expansion.

Example 10.17

graphics/10prfig01.gif

EXPLANATION

  1. The FCEDIT variable can be assigned the pathname for any of the UNIX text editors you have on your system, such as vi, emacs, textedit, etc. If not set, the ed editor is the default.

  2. The pwd command is typed at the command line. It will be placed in the history file.

  3. The fc command caused the editor (set in FCEDIT) to be invoked with the last command typed in the editor window. If the user writes and quits the editor, any commands typed there will be executed.

  4. The history command lists recently typed commands.

  5. The fc command is used to start up the editor with the last three commands from the history file in the editor's buffer.

10.1.7 Commenting and Filename Expansion

Filename expansion is a mechanism that allows the user to type part of a filename and press the escape key to see the rest of the filename(s). In the examples, [Esc] represents the escape key.

Table 10.4. Using the Esc Key and Filename Expansion
Combination Result
command [Esc]# # precedes command with a #; puts it on the history list commented; command will not be executed.
command [Esc]_ Underscore inserts the last word of the last command at the cursor position.
command [Esc] 2_ Inserts the second word of the last command at the cursor position.
word[Esc] * *replaces the current word with all files matched.
word[Esc] \ \ replaces the current word with the first filename that starts with the same characters; filename expansion.
word[Esc]= Displays all filenames beginning with the same character as the current word and displays them in a numbered list.
Example 10.18
    (Press the Esc Key for [Esc]. 1   $ ls a[Esc]=     1) abc     2) abc1     3) abc122     4) abc123     5) abc2 2   $ ls a[Esc]*     ls abc abc1 abc122 abc123 abc2     abc   abc1  abc122 abc123 abc2 3   $ print apples pears peaches     apples pears peaches 4   $ print [Esc]_     print peaches     peaches 5   $ print apples pears peaches plums    apples pears peaches 6   $ print [Esc]2_     print pears     pears 

EXPLANATION

  1. By typing an a, followed by the Esc key and an equal sign (=), all files starting with a are numbered and listed. (The numbers do not really serve any special purpose.)

  2. By typing an a, then the Esc key and an asterisk (*), the filenames starting with a are displayed.

  3. The print command displays its arguments: apples, pears, and peaches.

  4. The Esc key, followed by an underscore (_), is replaced with the last argument.

  5. The print command displays its arguments: apples, pears, and peaches.

  6. The Esc key, followed by the number 2 and an underscore, is replaced by the second argument. The command (print) is the first argument, starting at word zero.

10.1.8 Aliases

An alias is a Korn shell or user-defined abbreviation for a command. The alias name contains alphanumeric characters. Default aliases are provided by the shell and can be redefined or unset. Unlike the C shell aliases, the Korn shell does not support passing arguments. (If you need to use arguments, see "Functions".)

Aliases can be exported to subshells by storing them in the ENV file. (The commands in the ENV file are executed every time a new shell is spawned.) In the 1988 version of the Korn shell, the x option allows aliases to be exported to subshells as long as the new shell is not a separate invocation of ksh. Tracked aliases are provided by the Korn shell to speed up the time it takes the shell to search the path. Aliases can alias themselves; that is, they are recursive.

Listing Aliases. The alias built-in command lists all set aliases.

Example 10.19
1   $ alias 2   autoload=typeset  fu 3   false=let 0 4   functions=typeset  f 5   hash=alias  t 6   history=fc  l 7   integer=typset  i 8   r=fc  e  9   stop=kill  STOP 10  suspend=kill  STOP $$ 11  true=: 12  type=whence  v 

EXPLANATION

  1. The alias command, without arguments, lists all aliases. This is a list of preset aliases, including those you have set.

  2. The autoload alias is used for invoking functions dynamically.

  3. The false alias is used in expressions testing for a false condition.

  4. The functions alias lists all functions and their definitions.

  5. The hash alias lists all tracked aliases.

  6. The history alias lists all commands in the history file, .sh_history, preceded by a number.

  7. The integer alias allows you to create integer-type variables.

  8. The r alias lets you redo a previous command from the history list.

  9. The stop alias causes a process to be suspended if a job number or PID is provided to the kill command. The job can be resumed in the foreground by typing fg.

  10. The suspend alias suspends the current job by sending the STOP signal and the PID ($$) of this process to the kill command.

  11. The true alias is set to the do-nothing command, often used to start an infinite loop.

  12. The type alias indicates the type of command you are executing: an alias, a binary executable, and so forth.

Creating an Alias. The user can create aliases in the Korn shell. An alias is a nickname for an existing command or commands. The real command(s) is substituted for the alias when the shell evaluates the command line.

Example 10.20
1   $ alias cl='clear' 2   $ alias l='ls  laF' 3   $ alias ls='ls -aF' 4   $ \ls .. 

EXPLANATION

  1. The alias cl is an alias for clear.

  2. The alias is l. The letter l is a nickname for the command ls laF.

  3. The alias ls is assigned the command ls aF.

  4. The backslash turns off the meaning of the alias for the execution of this line. The real ls command, not the alias, is executed.

Removing an Alias. The unalias command deletes an alias.

Example 10.21
unalias cl 

EXPLANATION

The alias cl is removed from the list of set aliases.

Tracked Aliases. To reduce the amount of time needed to do a search of the path, the Korn shell creates an alias when a command is first encountered and sets the alias equal to the full pathname of the command. This is called a tracked alias.[3]

The Korn shell has some preset tracked aliases that are defined when it is installed. To use tracked aliases, the set o trackall command is issued; it is normally set in the ENV file. To see all tracked aliases, type alias t.

Example 10.22
$ alias  t chmod=/bin/chmod ls=/bin/ls vi=/usr/ucb/vi who=/bin/who 

EXPLANATION

The t option to the built-in alias command displays those commands that have been aliased via the tracking mechanism. When the user types any of these commands, the shell will not search the path, but use the alias definition to invoke the command.

10.1.9 Job Control

Job control is used to control the execution of background and foreground jobs.

To use Korn shell job control, the monitor option (set o monitor) must be set on systems that do not support job control. See Table 10.5 for job control commands.

Example 10.23
1   $ vi     [1]  + Stopped           # vi 2   $ sleep 25&     [2] 4538 3   $ jobs     [2] +  Running           # sleep 25&     [1]    Stopped           # vi 4   $ jobs  l     [2] + 4538   Running     # sleep 25&     [1]   4537   Stopped     # vi 5   $ fg %1 

EXPLANATION

  1. After the vi editor is invoked, you can press ^Z (Control-Z) to suspend the vi session. The editor will be suspended in the background, and after the message Stopped appears, the shell prompt will appear immediately.

  2. The ampersand at the end of the command causes the sleep command, with an argument of 25, to execute in the background. The notation [2] means that this is the second job to be run in the background and the PID of this job is 4538.

  3. The jobs command displays the jobs currently in the background.

  4. The jobs command with the l option displays the processes (jobs) running in the background and the PID numbers of those jobs.

  5. The fg command followed by a percent sign and the job number will bring that numbererd job into the foreground. Without a number, fg brings the most recently backgrounded job back into the foreground.

 

Table 10.5. Job Control Commands
Command Meaning
jobs Lists all unfinished processes in a numerically ordered list where the number of the job is enclosed in brackets.
jobs l Same as jobs, but includes the PID number of the job.
^Z Stops the current job.
fg %n Runs background job in foreground.
bg %n Runs job in background.
wait %n Waits for job number n to finish.
kill %n Kills job number n.

10.1.10 Metacharacters

Metacharacters are special characters used to represent something other than themselves. Table 10.6 lists some common Korn shell metacharacters and their functions.

Table 10.6. Shell Metacharacters
Command Function
\ Literal interpretation of the following character.
& Background processing.
; Command separator.
$ Variable substitution.
? Match for a single character.
[abc] Match for one from a set of characters.
[!abc] Match for one not from the set of characters.
* Match for zero or more characters.
(cmds ) Execute commands in a subshell.
{cmds} Execute commands in current shell.
Example 10.24
1   $ ls -d *    all files are displayed     abc   abc122   abc2  file1.bak file2.bak nonsense  nothing one     abc1 abc123   file1   file2   none    noone 2   $ print hello \     # The carriage return is escaped     > there     hello there 3   $ rusers&           # Process the rusers command in the background     [1]   4334     $ 4   $ who; date; uptime     # Commands are executed one at a time     ellie  console Feb 10 10:46     ellie  ttyp0   Feb 15 12:41     ellie  ttyp1   Feb 10 10:47     ellie  ttyp2   Feb 5  10:53     Mon Feb 15 17:16:43 PST 2001     5:16pm up 5 days, 6:32, 1 user, load average: 0.28, 0.23, 0.01 5   $ print $HOME       # The value of the HOME variable is printed     /home/jody/ellie 6   $ print $LOGNAME  # The value of the LOGNAME variable is printed     ellie 7   $ ( pwd; cd / ; pwd )     /home/jody/ellie     /     $ pwd     /home/jody/ellie 8   $ { pwd; cd /; pwd; }     /home/jody/ellie     /     $ pwd     / 9   $ ( date; pwd; ls ) > outfile     $ cat outfile     Mon Feb 15 15:56:34 PDT 2001     /home/jody/ellie     foo1     foo2     foo3 

EXPLANATION

  1. The asterisk matches all the files in the current directory. (The d option to the ls command prevents the contents of subdirectories from being displayed.)

  2. The backslash escapes the newline so that the command line can be continued on the next line.

  3. The ampersand (&) causes the rusers program to be executed in the background; the shell prompt returns immediately. Both processes will run concurrently.

  4. Each command is separated by a semicolon. Each command will be executed one at a time.

  5. If a dollar sign precedes the variable, the shell will perform variable substitution. The value of the env variable, HOME, is displayed. The HOME variable contains the full pathname of the user's home directory.

  6. Again, the dollar sign precedes the variable name. The value of the LOGNAME variable is the user's login name.

  7. The parentheses indicate that the enclosed commands will be executed in a subshell. The cd command is built into the shell, so that each shell that is invoked has its own copy of cd. The pwd command prints the present working directory, /home/jody/ellie. After cding to the root directory, the pwd command displays that the new directory is root. After the subshell exits, the output of the pwd command in the parent shell indicates that the directory is still set to /home/jody/ellie as it was before entering the subshell.

  8. The curly braces indicate that the enclosed commands will be executed in the current shell. The pwd command prints the present working directory, /home/jody/ellie. After cding to the root directory, the pwd command displays that the new directory is root. After the commands within the curly braces exit, the output of the pwd command indicates that the directory is still set to the root directory.

  9. The parentheses are used to group the commands so that the output of all three commands is sent to outfile.

10.1.11 Filename Substitution (Wildcards)

When evaluating the command line, the shell uses metacharacters to abbreviate filenames or pathnames that match a certain set of characters, often called wildcards. The filename substitution metacharacters listed in Table 10.7 are expanded into an alphabetically listed set of filenames. The process of expanding a metacharacter into filenames is also called filename substitution, or globbing. If a metacharacter is used and there is no filename that matches it, the Korn shell treats the metacharacter as a literal character.

Table 10.7. Shell Metacharacters and Filename Substitution
Metacharacter Meaning
* Matches zero or more characters.
? Matches exactly one character.
[abc] Matches one character in the set, a, b, or c.
[a z] Matches one character in the range: any character in the set between a and z.
~ Substitutes the user's home directory for ~.
\ Escapes or disables the metacharacter.

The Asterisk. The asterisk is a wildcard that matches for zero or more of any character in a filename.

Example 10.25
1   $ ls *     abc abc1 abc122 abc123 abc2 file1 file1.bak file2 file2.bak none     nonsense noone nothing nowhere one 2   $ ls *.bak     file1.bak file2.bak 3   $ print a*c     abc 

EXPLANATION

  1. The asterisk expands to all of the files in the present working directory. All of the files are passed to ls and displayed.

  2. All files starting with zero or more characters and ending with .bak are matched and listed.

  3. All files starting with a, followed by zero or more characters, and ending in c are matched and passed as arguments to the print command.

The Question Mark. The question mark represents a single character in a filename. When a filename contains one or more question marks, the shell performs filename substitution by replacing the question mark with the character it matches in the filename.

Example 10.26
1   $ ls     abc abc1 abc122 abc123 abc2 file1 file1.bak file2 file2.bak     none nonsense noone nothing nowhere one 2   $ ls a?c?     abc1 abc2 3   $ ls ??     ?? not found 4   $ print abc???     abc122 abc123 5   $ print ??     ?? 

EXPLANATION

  1. The files in the current directory are listed.

  2. Filenames containing four characters are matched and listed if the filename starts with an a, followed by a single character, followed by a c and a single character.

  3. Filenames containing exactly two characters are listed. There are none, so the two question marks are treated as literal characters. Since there is no file in the directory called ??, the shell sends the message ?? not found.

  4. Filenames containing six characters are matched and printed, starting with abc and followed by exactly three of any character.

  5. The ksh print function gets the two question marks as an argument. The shell tries to match for any filenames with exactly two characters. There are no files in the directory that contain exactly two characters. The shell treats the question mark as a literal question mark if it cannot find a match. The two literal question marks are passed as arguments to the print command.

The Square Brackets. Brackets are used to match filenames containing one character from a set or range of characters.

Example 10.27
1   $ ls     abc abc1 abc122 abc123 abc2 file1 file1.bak file2 file2.bak     none nonsense noone nothing nowhere one 2   $ ls abc[123]     abc1 abc2 3   $ ls abc[1 3]     abc1 abc2 4   $ ls [a z][a z][a z]     abc one 5   $ ls [!f z]???     abc1 abc2 6   $ ls abc12[2-3]     abc122 abc123 

EXPLANATION

  1. All of the files in the present working directory are listed.

  2. All four-character names are matched and listed if the filename starts with abc, followed by 1, 2, or 3. Only one character from the set in the brackets is matched for a filename.

  3. All four-character filenames are matched and listed if the filename starts with abc, and is followed by a number in the range from 1 to 3.

  4. All three-character filenames are matched and listed, if the filename contains exactly three lowercase alphabetic characters.

  5. All four-character files are listed if the first character is not a letter between f and z, followed by three of any character (???).

  6. Files are listed if the filenames contain abc12, followed by 2 or 3.

Escaping the Metacharacters. To use a metacharacter as a literal character, use the backslash to prevent the metacharacter from being interpreted.

Example 10.28
1   $ ls     abc file1 youx 2   $ print How are you?     How are youx 3   $ print How are you\?     How are you? 4   $ print When does this line \     > ever end\?     When does this line ever end? 

EXPLANATION

  1. The files in the present working directory are listed. Note the file youx.

  2. The shell will perform filename expansion on the question mark. Any files in the current directory starting with y-o-u and followed by exactly one character are matched and substituted in the string. The filename youx will be substituted in the string to read, How are youx (probably not what you wanted to happen).

  3. By preceding the question mark (?) with a backslash, it is escaped, meaning that the shell will not try to interpret it as a wildcard.

  4. The newline is escaped by preceding it with a backslash. The secondary prompt is displayed until the string is terminated with a newline. The question mark (?) is escaped to protect it from filename expansion.

Tilde and Hyphen Expansion. The tilde character was adopted by the Korn shell (from the C shell ) for pathname expansion. The tilde by itself evaluates to the full pathname of the user's home directory. When the tilde is appended with a username, it expands to the full pathname of that user

The hyphen character refers to the previous working directory; OLDPWD also refers to the previous working directory.

Example 10.29
1   $ echo ~     /home/jody/ellie 2   $ echo ~joe     /home/joe 3   $ echo ~+     /home/jody/ellie/perl 4   $ echo ~      /home/jody/ellie/prac 5   $ echo $OLDPWD     /home/jody/ellie/prac 6   $ cd       /home/jody/ellie/prac 

EXPLANATION

  1. The tilde evaluates to the full pathname of the user's home directory.

  2. The tilde preceding the username evaluates to the full pathname of joe's home directory.

  3. The ~+ notation evaluates to the full pathname of the working directory.

  4. The ~ notation evaluates to the previous working directory.

  5. The OLDPWD variable contains the previous working directory.

  6. The hyphen refers to the previous working directory; cd to go to the previous working directory and display the directory.

New ksh Metacharacters. The new Korn shell metacharacters are used for filename expansion in a way that is similar to the regular expression metacharacters of egrep and awk. The metacharacter preceding the characters enclosed in parentheses controls what the pattern matches. See Table 10.8.

Example 10.30
1   $ ls     abc abc1 abc122 abc123 abc2 file1 file1.bak file2 file2.bak none     nonsense noone nothing nowhere one 2   $ ls abc?(1|2)     abc  abc1 abc2 3   $ ls abc*([1 5])     abc   abc1  abc122 abc123 abc2 4   $ ls abc+([1 5])     abc1  abc122 abc123 abc2 5   $ ls no@(thing|ne)     none   nothing 6   $ ls no!(one|nsense)     none  nothing nowhere 

EXPLANATION

  1. All the files in the present working directory are listed.

  2. Matches filenames starting with abc and followed by zero characters or one of either of the patterns in parentheses. Matches abc , abc1, or abc2.

  3. Matches filenames starting with abc and followed by zero or more numbers between 1 and 5. Matches abc, abc1, abc122, abc123, and abc2.

  4. Matches filenames starting with abc and followed by one or more numbers between 1 and 5. Matches abc1, abc122, abc123, and abc2.

  5. Matches filenames starting with no and followed by exactly thing or ne. Matches nothing or none.

  6. Matches filenames starting with no and followed by anything except one or nsense. Matches none, nothing, and nowhere.

 

Table 10.8. Regular Expression Wildcards
Regular Expression Meaning
abc?(2|9)1 ? matches zero or one occurrences of any pattern in the parentheses. The vertical bar represents an or condition; e.g., either 2 or 9. Matches abc21, abc91, or abc1.
abc*([0 9]) * matches zero or more occurrences of any pattern in the parentheses. Matches abc followed by zero or more digits; e.g., abc, abc1234, abc3, abc2, etc.
abc+([0 9]) + matches one or more occurrences of any pattern in the parentheses. Matches abc followed by one or more digits; e.g., abc3, abc123, etc.
no@(one|ne) @ matches exactly one occurrence of any pattern in the parentheses. Matches noone or none.
no!(thing|where) ! matches all strings except those matched by any of the pattern in the parentheses. Matches no, nobody, or noone, but not nothing or nowhere.

The noglob Variable. If the noglob variable is set, filename substitution is turned off, meaning that all metacharacters represent themselves; they are not used as wildcards. This can be useful when searching for patterns containing metacharacters in programs like grep, sed, or awk. If noglob is not set, all metacharacters must be escaped with a backslash if they are not to be interpreted.

Example 10.31
1   % set -o noglob       # or set -f 2   % print * ?? [] ~ $LOGNAME     * ?? [] /home/jody/ellie ellie 3   % set +o noglob   # or set +f 

EXPLANATION

  1. The noglob variable is set. It turns off the special meaning of the wildcards for filename expansion. You can use the f option to set the command to achieve the same results.

  2. The filename expansion metacharacters are displayed as themselves without any interpretation. Note that the tilde and the dollar sign are still expanded.

  3. The noglob option is reset. Filename metacharacters will be expanded.

10.1.12 Variables

Local Variables. Local variables are given values that are known only to the shell in which they are created. Variable names must begin with an alphabetic or underscore character. The remaining characters can be alphabetic, decimal digits zero to nine, or an underscore character. Any other characters mark the termination of the variable name.

Setting and Referencing Local Variables. When assigning a value to a variable, there can be no whitespace surrounding the equal sign. To set the variable to null, the equal sign is followed by nothing. If more than one word is assigned to a variable, it must be quoted to protect the whitespace; otherwise, the shell prints an error message and the variable is undefined.

If a dollar sign is prepended to the variable name, the value assigned to that variable can be referenced. If other characters are attached to a variable name, curly braces are used to shield the name of the variable from the extra characters.

Example 10.32
1   $ state=Cal     $ echo $state     Cal 2   $ name="Peter Piper"     $ echo $name     Peter Piper 3   $ x=     $ echo $x # Blank line appears when a variable is either unset or set to null     $ 4   $ state=Cal     $ print ${state}ifornia     California 

EXPLANATION

  1. The variable state is assigned the value Cal . When the shell encounters the dollar sign preceding a variable name, it performs variable substitution. The value of the variable is displayed.

  2. The variable name is assigned the value "Peter Piper". The quotes are needed to hide the whitespace so that the shell will not split the string into separate words when it parses the command line. The value of the variable is displayed.

  3. The variable x is not assigned a value. It will be assigned a null string. The null value, an empty string, is displayed. The same output would be displayed if the variable had not been set at all.

  4. The variable state is assigned the value Cal. The variable is enclosed in curly braces to shield it from the characters that are appended. The value of the variable Cal is displayed with ifornia appended.

The Scope of Local Variables. A local variable is known only to the shell in which it was created. It is not passed on to subshells. The $$ variable is a special variable containing the PID (process identification number) of the current shell.

Example 10.33
1   $ echo $$     1313 2   $ round=world     $ echo $round     world 3   $ ksh     # Start a subshell 4   $ echo $$     1326 5   $ echo $round 6   $ exit       # Exits this shell, returns to parent shell 7   $ echo $$     1313 8   $ echo $round     world 

EXPLANATION

  1. The value of the $$ variable evaluates to the PID of the current shell. The PID of this shell is 1313.

  2. The local variable round is assigned the string value world and the value of the variable is displayed.

  3. A new Korn shell is invoked. This is called a subshell, or child shell.

  4. The PID of this shell is 1326. The parent shell's PID is 1313.

  5. The variable round is not defined in this shell.

  6. The exit command terminates this shell and returns to the parent shell. If the ignoreeof option is not set, Control-D will also exit this shell.

  7. The parent shell returns. Its PID is displayed.

  8. The value of the variable is displayed.

Setting Read-Only Variables. A read-only variable cannot be redefined or unset. It can be set with the readonly or typeset r built-in commands. You may want to set variables to readonly for security reasons when running in privileged mode.

Example 10.34
1   $ readonly name=Tom     $ print $name     Tom 2   $ unset name     ksh: name: is read only 3   $ name=Joe     ksh name: is read only 4   $ typeset -r PATH     $ PATH=${PATH}:/usr/local/bin     ksh: PATH: is read only 

EXPLANATION

  1. The readonly local variable name is assigned the value Tom.

  2. A readonly variable cannot be unset.

  3. A readonly variable cannot be redefined.

  4. The PATH variable is set to be readonly. Any effort to unset or change the variable will produce an error message.

Environment Variables. Environment variables are available to the shell in which they are created and any subshells or processes spawned from that shell. By convention, environment variables are capitalized.

The shell in which a variable is created is called the parent shell. If a new shell is started from the parent shell, it is called the child shell. Some of the environment variables, such as HOME, LOGNAME, PATH, and SHELL, are set before you log in by the /bin/login program. Normally, environment variables are set in the .profile file in the user's home directory.

Setting Environment Variables. To set environment variables, the export command is used either after assigning a value or when the variable is set. All variables in a script can be exported by turning on the allexport option to the set command.

Example 10.35
1   $ TERM=wyse ; export TERM 2   $ export NAME ="John Smith"     $ print $NAME     John Smith 3   $ print $$     319 4   $ ksh 5   $ print $$     340 6   $ print $NAME     John Smith 7   $ NAME="April Jenner"     $ print $NAME     April Jenner 8   $ exit 9   $ print $$     319 10  $ print $NAME     John Smith 

EXPLANATION

  1. The TERM variable is assigned wyse. The variable is exported. Now processes started from this shell will inherit the variable.

  2. The variable is exported and defined in the same step. (New with the Korn shell.)

  3. The value of this shell's PID is printed.

  4. A new Korn shell is started. The new shell is called the child. The original shell is its parent.

  5. The PID of the new Korn shell, stored in the $$ (340) variable, is printed.

  6. The variable was exported to the new shell and is displayed.

  7. The variable is reset to April Jenner and displayed.

  8. This Korn child shell is exited. The parent shell will return.

  9. The PID of the parent, 319, is displayed again.

  10. The variable NAME contains its original value. Variables retain their values when exported from parent to child. The child cannot change the value of a variable for its parent.

Special Environment Variables. The Korn shell assigns default values to the environment variables, PATH, PS1, PS2, PS3, PS4, MAILCHECK, FCEDIT, TMOUT, and IFS. The SHELL, LOGNAME, USER, and HOME are set by the /bin/login program. You can change the values of the defaults and set the others listed in Table 10.9.

Table 10.9. Korn Shell Environment Variables
Variable Name Meaning
_ (underscore) The last argument of the previous command.
CDPATH The search path for the cd command. A colon-separated list of directories used to find a directory if the / , ./, or ../ is not at the beginning of the pathname.
COLUMNS If set, defines the width of the edit window for shell edit modes and the select command.
EDITOR Pathname for a built-in editor: emacs, gmacs, or vi.
ENV A variable set to the name of a file, containing functions and aliases, that the Korn shell will invoke when the ksh program is invoked. On versions newer than 1988, this file is only executed when ksh is invoked interactively, not for noninteractive shells. The variable is not expanded if the privileged option is turned on.
ERRNO System error number. Its value is the error number of the most recently failed system call.
FCEDIT Default editor name for the fc command. On versions newer than 1988, this variable is called HISTEDIT, and the fc command is hist.
FPATH A colon-separated list of directories that defines the search path for directories containing auto-loaded functions.
HISTEDIT For versions of the Korn shell newer than 1988, the new name for FCEDIT.
HISTFILE Specifies file in which to store command history.
HISTSIZE Maximum number of commands from the history that can be accessed; default is 128.
HOME Home directory; used by cd when no directory is specified.
IFS Internal field separators, normally SPACE, TAB, and NEWLINE, used for field splitting of words resulting from command substitution, lists in loop constructs, and reading input.
LINENO Current line number in script.
LINES Used in select loops for vertically displaying menu items; default is 24.
MAIL If this parameter is set to the name of a mail file and the MAILPATH parameter is not set, the shell informs the user of the arrival of mail in the specified file.
MAILCHECK This parameter specifies how often (in seconds) the shell will check for the arrival of mail in the files specified by the MAILPATH or MAIL parameters. The default value is 600 seconds (10 minutes). If set to zero, the shell will check before issuing each primary prompt.
MAILPATH A colon-separated list of filenames. If this parameter is set, the shell informs the user of the arrival of mail in any of the specified files. Each filename can be followed by a % and a message that will be printed when the modification time changes. The default message is You have mail.
OLDPWD Last working directory.
PATH The search path for commands; a colon-separated list of directories the shell uses to search for the command you want to execute.
PWD Present working directory; set by cd.
PPID Process ID of the parent process.
PS1 Primary prompt string, by default $.
PS2 Secondary prompt string, by default >.
PS3 Selection prompt string used with the select command, by default #?.
PS4 Debug prompt string used when tracing is turned on, by default +.
RANDOM Random number generated each time the variable is referenced.
REPLY Set when read is not supplied arguments.
SHELL When the shell is invoked, it scans the environment for this name. The shell gives default values to PATH, PS1, PS2, MAILCHECK, and IFS. HOME and MAIL are set by login.
TMOUT Specifies number of seconds to wait for input before exiting.
VISUAL Specifies editor for in-line command editing: emacs, gmacs, or vi.

Listing Set Variables. There are three built-in commands that print the value of variables: set, env, and typeset. The set command prints all variables, local and global. The env command prints only global variables. The typeset command prints all variables, integers, functions, and exported variables. The set -o command prints all options set for the Korn shell.

Example 10.36
1   $ env       # Partial list     LOGNAME=ellie     TERMCAP=sun cmd:te=\E[>4h:ti=\E[>4l:tc=sun:     USER=ellie     DISPLAY=:0.0     SHELL=/bin/ksh     HOME=/home/jody/ellie     TERM=sun cmd     LD_LIBRARY_PATH=/usr/local/OW3/lib     PWD=/home/jody/ellie/perl 2   $ typeset     export MANPATH     export PATH     integer ERRNO     export FONTPATH     integer OPTIND     function LINENO     export OPENWINHOME     export LOGNAME     function SECONDS     integer PPID     PS3     PS2     export TERMCAP     OPTARG     export USER     export DISPLAY     function RANDOM     export SHELL     integer TMOUT     integer MAILCHECK 3   $ set     DISPLAY=:0.0     ERRNO=10     FCEDIT=/bin/ed     FMHOME=/usr/local/Frame 2.1X     FONTPATH=/usr/local/OW3/lib/fonts     HELPPATH=/usr/local/OW3/lib/locale:/usr/local/OW3/lib/help     HOME=/home/jody/ellie     IFS=     LD_LIBRARY_PATH=/usr/local/OW3/lib     LINENO=1     LOGNAME=ellie     MAILCHECK=600     MANPATH=/usr/local/OW3/share/man:/usr/local/OW3/man:/     usr/local/man:/usr/local/doctools/man:/usr/man     OPTIND=1     PATH=/home/jody/ellie:/usr/local/OW3/bin:/usr/ucb:/     usr/local/doctools/bin:/usr/bin:/usr/local:/usr/etc:/etc:/     usr/spool/news/bin:/home/jody/ellie/bin:/usr/lo     PID=1332     PS1=$     PS2=>     PS3=#?     PS4=+     PWD=/home/jody/ellie/kshprog/joke     RANDOM=4251     SECONDS=36     SHELL=/bin/ksh     TERM=sun cmd     TERMCAP=sun cmd:te=\E[>4h:ti=\E[>4l:tc=sun:         TMOUT=0     USER=ellie     _=pwd     name=Joe     place=San Francisco     x= 4   set -o     allexport      off     bgnice        on     emacs         off     errexit       off     gmacs         off     ignoreeof     off     interactive   on     keyword       off     markdirs      off     monitor       on     noexec        off     noclobber     off     noglob        off     nolog         off     nounset       off     privileged    off     restricted    off     trackall      off     verbose       off     viraw         off     xtrace        off 

EXPLANATION

  1. The env command lists all environment (exported) variables. These variables are, by convention, named with uppercase letters. They are passed from the process in which they are created to any of the child processes.

  2. The typeset command displays all variables and their attributes, functions, and integers. The typeset command with the + option displays only the names of the variables.

  3. The set command, without options, prints all set variables, local and exported, including variables set to null.

  4. The set command with the o option lists all built-in variables that are set to on or off. To turn options off, use the plus sign (+), and to turn options on, use the minus sign (-); for example, set -o allexport turns on the allexport option.

Unsetting Variables. Both local and environment variables can be unset by using the unset command (unless the variables are set to readonly).

Example 10.37
unset name; unset TERM 

EXPLANATION

The variables name and Term are no longer defined for this shell.

Printing the Values of Variables. The echo command (used in Bourne and C shells) is still effective in this shell, but the print command has more options and is more efficient. Both the echo command and print command are built-in to the shell. The print command has a number of options to control its output. They are listed in Table 10.10.

Table 10.10. Print Options
Option Meaning
r Prevents escape sequences.
R Prevents ksh from treating a 2 or x as a print argument; turns off the dash if preceding an argument (except n); \t, \c, \c are not recognized as special and appear unchanged when printed without the \.
un Redirects output to file descriptor n.
n No newline in output; like echo n.
p Sends output to a coprocess or pipe (&|) rather than to standard output.
s Output is appended to the history file as a command rather than to standard output.
Any arguments that follow are not print options. The dash allows arguments that contain a hyphen, e.g., 2.
f For versions newer than 1988, used to emulate printf.
Example 10.38
1   $ print Hello my friend and neighbor!     Hello my friend and neighbor! 2   $ print "Hello         friends"     Hello       friends 3   $ print  r "\n"     \n 4   $ print -s "date +%H"     $ history -2     132 print -s "date +%H"     133 date +%H     134 history -2     $ r 133     09 5   $ print  n $HOME     /home/jody/ellie 6   $ var=world     $ print ${var}wide     worldwide 7   $ print  x is an option     ksh: print: bad option(s) 8   $ print     x is an option      x is an option 

EXPLANATION

  1. The shell parses the command line, breaks the command line into words (tokens) separated by space, and passes the words as arguments to the print command. The shell removes all the extra whitespace between words.

  2. The quotes create a single string. The shell evaluates the string as a single word and the whitespace is preserved.

  3. This is the raw option. Any escape sequences, such as \r, are not interpreted.

  4. The s option appends the print command's arguments to the history file as a command. The string "date +%H" is the argument to the print command. The date string is appended to the history list as a command and then executed with the r command (history's redo command).

  5. The n option suppresses the newline. The Korn shell prompt is on the same line as the output from the print command.

  6. The local variable is assigned the value world. The braces insulate the variable from characters appended to it.

  7. When the first argument to the print function begins with a dash, the print command interprets the argument as one of its options, unless an additional preceding dash is provided.

  8. The dash as an option allows you to use a dash as the first character in the string to be printed.

Escape Sequences. Escape sequences consist of a character preceded by a backslash and have a special meaning when enclosed within quotes. (See Table 10.11.)

The print command, without the -r and -R options, formats output when any of the following escape sequences are placed within a string. The string must be enclosed in double or single quotes.

Example 10.39
1   $ print '\t\t\tHello\n'            Hello     $ 2   $ print "\aTea \tTime!\n\n"     Ding ( bell rings ) Tea     Time! 

EXPLANATION

  1. The backslash characters must be quoted with either single or double quotes. The \t escape sequence represents a tab, and \n represents a newline. The output is three tabs, followed by the string Hello, and a newline.

  2. The \a escape sequence causes a bell to ring (\07) and the \t creates a tab. The two \n sequences will print two newline characters.

 

Table 10.11. Escape Sequences
Backslash Character Meaning
\a Bell character.
\b Backspace.
\c Suppress newline and ignore any arguments that follow \c.
\f Formfeed.
\n Newline.
\r Return.
\t Tab.
\v Vertical tab.
\\ Backslash.
\0x Eight-bit character with a 1-, 2-, or 3-digit ASCII value, as in print \0124.
\E Only on versions newer than 1988; used for an escape sequence.

Variable Expressions and Expansion Modifiers. Variable expressions can be tested and modified by using special modifiers. The modifier provides a shortcut conditional test to check whether a variable has been set, and then, depending on the modifier, may assign a default value to the variable. These expressions can be used with conditional constructs such as if and elif. See Table 10.12.

The colon does not have to be used with the modifier. Using the colon with the modifier checks whether the variable is not set or is null; without the colon, a variable set to null is considered a set variable.

Table 10.12. Variable Expressions and Modifiers
Expression Function
${variable: word} If variable is set and is nonnull, substitute its value; otherwise, substitute word.
${variable:=word} If variable is not set or is null, set it to word; the value of variable is substituted permanently. Positional parameters may not be assigned in this way.
${variable:+word} If variable is set and is nonnull, substitute word; otherwise substitute nothing.
${variable:?word} If variable is set and is nonnull, substitute its value; otherwise, print word and exit from the shell. If word is omitted, the message parameter null or not set is printed.
Example 10.40
(Using Temporary Default Values) 1   $ fruit=peach 2   $ print ${fruit: plum}     peach 3   $ print ${newfruit: apple}     apple 4   $ print $newfruit 5   $ print ${TERM:-vt120}     sun-cmd 

EXPLANATION

  1. The variable fruit is assigned the value peach.

  2. The special modifier will check to see if the variable fruit has been set. If it has, the value peach is printed; if not, plum is printed.

  3. The variable newfruit has not been set. The value apple will be printed.

  4. The variable newfruit was not set, so nothing prints. In step 3, the expression was simply replaced with the word apple and printed.

  5. If the TERM variable has not been set, a default value vt120 will be displayed. In this example, the terminal has already been set to sun cmd, a Sun workstation.

Example 10.41
(Assigning Permanent Default Values) 1   $ name= 2   $ print ${name:=Patty}     Patty 3   $ print $name     Patty 4   $ print ${TERM:=vt120}     vt120     $ print $TERM     vt120 

EXPLANATION

  1. The variable name is assigned the value null.

  2. The special modifier := will check to see if the variable name has been set to some value other than null. If it has been set, it will not be changed; if it is either null or not set, it will be assigned the value to the right of the equal sign. Patty is assigned to name since the variable is set to null. The setting is permanent.

  3. The variable name still contains the value Patty.

  4. If the variable TERM is not set, it will be assigned the default value vt120 permanently.

Example 10.42
(Assigning Temporary Alternate Value) 1   $ foo=grapes 2   $ print ${foo:+pears}     pears     $ print $foo     grapes 

EXPLANATION

  1. The variable foo has been assigned the value grapes.

  2. The special modifier :+ will check to see if the variable has been set. If it has been set, it will temporarily be reset to grapes. If it has not been set, null is returned.

Example 10.43
(Creating Error Messages Based on Default Values) 1   $ print ${namex:?"namex is undefined"}     ksh: namex: namex is undefined 2   $ print ${y?}     ksh: y: parameter null or not set 

EXPLANATION

  1. The :? modifier will check to see if the variable has been set. If not, the string to the right of the ? is printed to standard error, after the name of the variable. If in a script, the script exits.

  2. If a message is not provided after the ?, the Korn shell sends a default message to standard error. Without the colon, the ? modifier would consider a variable set to null a set variable, and the message would not be printed.

Example 10.44
(Line from a System Script) if [ "${uid:=0}" -ne 0 ] 

EXPLANATION

If the UID (user ID) has a value, it will not be changed; if it does not have a value, it will be assigned the value zero (superuser). The value of the variable will be tested for nonzero. This line was taken from the /etc/shutdown program (SVR4/Solaris 2.5). It is here to give you an example of how variable modifiers are used.

Variable Expansion of Substrings. Pattern matching arguments are used to strip off certain portions of a string from either the front or end of the string. The most common use for these operators is stripping off pathname elements from the head or tail of the path. See Table 10.13.

Table 10.13. Variable Expansion Substrings
Expression Function
${variable%pattern} Matches the smallest trailing portion of the value of variable to pattern and removes it.
${variable%%pattern} Matches the largest trailing portion of the value of variable to pattern and removes it.
${variable#pattern} Matches the smallest leading portion of the value of variable to pattern and removes it.
${variable##pattern} Matches the largest leading portion of the value of variable to pattern and removes it.
Example 10.45
1   $ pathname="/usr/bin/local/bin" 2   $ print ${pathname%/bin*}     /usr/bin/local 

EXPLANATION

  1. The local variable pathname is assigned /usr/bin/local/bin.

  2. The % removes the smallest trailing portion of pathname containing the pattern /bin, followed by zero or more characters; that is, it strips off /bin.

Example 10.46
1   $ pathname="usr/bin/local/bin" 2   $ print ${pathname%%/bin*}     /usr 

EXPLANATION

  1. The local variable pathname is assigned /usr/bin/local/bin.

  2. The %% removes the largest trailing portion of pathname containing the pattern /bin, followed by zero or more characters; that is, it strips off /bin/local/bin

Example 10.47
1   $ pathname=/home/lilliput/jake/.cshrc 2   $ print ${pathname#/home}     /lilliput/jake/.cshrc 

EXPLANATION

  1. The local variable pathname is assigned /home/liliput/jake/.cshrc.

  2. The # removes the smallest leading portion of pathname containing the pattern /home; that is, /home is stripped from the beginning of the path variable.

Example 10.48
1   $ pathname=/home/liliput/jake/.cshrc 2   $ print ${pathname##*/}     .cshrc 

EXPLANATION

  1. The local variable pathname is assigned /home/liliput/jake/.cshrc.

  2. The ## removes the largest leading portion of pathname containing zero or more characters up to and including the last slash; that is, it strips off /home/lilliput/jake from the path variable.

Variable Attributes: The typeset Command. The attributes of a variable, such as its case, width, and left or right justification, can be controlled by the typeset command. When the typeset command changes the attributes of a variable, the change is permanent. The typeset function has a number of other functions. See Table 10.14.

Example 10.49
1   $ typeset  u name="john doe"     $ print "$name"     JOHN DOE             # Changes all characters to uppercase. 2   $ typeset  l name     $ print $name     john doe             # Changes all characters to lowercase. 3   $ typeset  L4 name     $ print $name     john              # Left-justified fixed-width 4-character field. 4   $ typeset  R2 name     $ print $name     # Right-justified fixed-width 2-character field.     hn 5   $ name="John Doe"     $ typeset  Z15 name        # Null padded sting, 15-space field.            width     $ print "$name"      John Doe 6   $ typeset  LZ15 name       # Left-justified, 15-space field width.     $ print "$name$name"     John Doe       John Doe 7   $ integer n=25     $ typeset -Z15 n            # Left-justified, zero-padded integer.     $ print "$n"     000000000000025 8   $ typeset  lL1 answer=Yes   # Left justify one lowercase letter.     $ print $answer     y 

EXPLANATION

  1. The u option to the typeset command converts all characters in a variable to uppercase.

  2. The l option to the typeset command converts all characters in a variable to lowercase.

  3. The L option to the typeset command converts the variable name to a left-justified, four-character string, john.

  4. The R option to the typeset command converts the variable name to a right-justified, two-character string, hn.

  5. The variable name is set to John Doe. The Z option to the typeset command will convert the string to a null-padded, 15-space string. The variable is quoted to preserve whitespace.

  6. The variable name is converted to a left-justified, 15-space, null-padded string.

  7. The variable n is an integer (see typeset i, Table 10.14) assigned the value 25. The typeset command will convert the integer n to a zero-filled, 15-space, left-justified number.

  8. The variable answer is assigned the value Yes and converted to a lowercase, left-justified, one-character string. (This can be very useful when handling user input in a script.)

 

Table 10.14. Other Uses of the typeset Command
Command What It Does
typeset Displays all variables.
typeset i num Will only accept integer values for num.
typeset x Displays exported variables.
typeset a b c If defined in a function, creates a, b, and c to be local variables.
typeset r x=foo Sets x to foo and then makes it read-only.

Positional Parameters. Normally, the special built-in variables, often called positional parameters, are used in shell scripts when passing arguments from the command line, or used in functions to hold the value of arguments passed to the function. The variables are called positional parameters because they are referenced by numbers 1, 2, 3, and so on, representing their respective positions in the parameter list. See Table 10.15.

The name of the shell script is stored in the $0 variable. The positional parameters can be set and reset with the set command.

Table 10.15. Positional Parameters
Expression Function
$0 References the name of the current shell script.
$1 $9 Positional parameters 1 9.
${10} Positional parameter 10.
$# Evaluates to the number of positional parameters.
$* Evaluates to all the positional parameters.
$@ Same as $*, except when double quoted.
"$*" Evaluates to "$1 $2 $3", etc.
"$@" Evaluates to "$1" "$2" "$3", etc.
Example 10.50
1   $ set tim bill ann fred     $ print $*           # Prints all the positional parameters.     tim bill ann fred 2   $ print $1           # Prints the first position.     tim 3   $ print $2 $3        # Prints the second and third position.     bill ann 4   $ print $#           # Prints the total number of positional     4                    # parameters. 5   $ set a b c d e f g h i j k l m     $ print $10          # Prints the first positional parameter     a0                   # followed by a 0.     $ print ${10} ${11}  # Prints the 10th and 11th positions.     j k 6   $ print $#     13 7   $ print $*     a b c d e f g h i j k l m 8   $ set file1 file2 file3     $ print \$$#     $3 9   $ eval print \$$#     file3 

EXPLANATION

  1. The set command assigns values to positional parameters. The $* special variable contains all of the parameters set.

  2. The value of the first positional parameter, tim, is displayed.

  3. The value of the second and third parameters, bill and ann, are displayed.

  4. The $# special variable contains the number of positional parameters currently set.

  5. The set command resets all of the positional parameters. The original parameter list is cleared. To print any positional parameters beyond 9, the curly braces are used to keep the two digits together. Otherwise, the value of the first positional parameter is printed, followed by the number appended to it.

  6. The number of positional parameters is now 13.

  7. The values of all the positional parameters are printed.

  8. The dollar sign is escaped; $# is the number of arguments. The print command displays $3, a literal dollar sign followed by the number of positional parameters.

  9. The eval command parses the command line a second time before executing the command. The first time parsed by the shell, the print would display $3; the second time, after eval, the print displays the value of $3, file3.

10.1.13 Other Special Variables

The Korn shell has some special built-in variables, as shown in Table 10.16.

Table 10.16. Special Variables
Variable Meaning
$$ PID of the shell.
$ ksh options currently set.
$? Exit value of last executed command.
$! PID of last job put in background.
Example 10.51
1   $ print The pid of this shell is $$     The pid of this shell is 4725 2   $ print The options for this korn shell are $      The options for this korn shell are ismh 3   $ grep dodo /etc/passwd     $ print $?     1 4   $ sleep 25&     [1]    400     $ print $!     400 

EXPLANATION

  1. The $$ variable holds the value of the PID for this process.

  2. The $ variable lists all options for this interactive Korn shell.

  3. The grep command searches for the string dodo in the /etc/passwd file. The ? variable holds the exit status of the last command executed. Since the value returned from grep is one, grep is assumed to have failed in its search. An exit status of zero indicates a successful exit.

  4. The & appended to the sleep command causes the command to be executed in the background. The $! variable holds the PID number of the last command placed in the background.

10.1.14 Quotes

Quotes are used to protect special metacharacters from interpretation. They can cause major debugging hassles in all shell scripts. Single quotes must be matched. They protect special metacharacters from being interpreted by the shell. Double quotes also must be matched. They protect most characters from interpretation by the shell, but allow variable and command substitution characters to be processed. Single quotes will protect double quotes, and double quotes will protect single quotes. The Korn shell, unlike the Bourne shell, will inform you if you have mismatched quotes by sending an error message to standard error with the line where it detects that the quotes were mismatched.

The Backslash. The backslash is used to protect (or escape) a single character from interpretation.

Example 10.52
1   $ print Where are you going\?     Where are you going? 2   $ print Start on this line and \     > go to the next line.     Start on this line and go to the next line. 

EXPLANATION

  1. The special metacharacter ? is escaped with the backslash. It will not be interpreted for filename expansion by the shell.

  2. The newline is escaped. The next line will become part of the first line. The > is the Korn shell's secondary prompt.

Single Quotes. Single quotes must be matched. They protect all metacharacters from interpretation. To print a single quote, it must be enclosed in double quotes or escaped with a backslash.

Example 10.53
    $ print 'hi there     > how are you?     > When will this end?     > When the quote is matched     > oh'     hi there     how are you?     When will this end?     When the quote is matched     oh 2   $ print 'Do you need $5.00?'     Do you need $5.00? 3   $ print 'Mother yelled, "Time to eat!"'     Mother yelled, "Time to eat!" 

EXPLANATION

  1. The single quote is not matched on the line. The Korn shell produces a secondary prompt. It is waiting for the quote to be matched.

  2. The single quotes protect all metacharacters from interpretation. In this example, the $ and the ? are protected from the shell and will be treated as literals.

  3. The single quotes protect the double quotes in this string. The double quotes here are conversational quotes.

Double Quotes. Double quotes must be matched, will allow variable and command substitution, and protect any other special metacharacters from being interpreted by the shell.[4]

Example 10.54
1   $ name=Jody 2   $ print "Hi $name, I'm glad to meet you!"     Hi Jody, I'm glad to meet you! 3   $ print "Hey $name, the time is 'date'"     Hey Jody, the time is Fri Dec 18 14:04:11 PST 2001 

EXPLANATION

  1. The variable name is assigned the string Jody.

  2. The double quotes surrounding the string will protect all special metacharacters from interpretation, with the exception of $ in $name. Variable substitution is performed within double quotes.

  3. Variable substitution and command substitution are both performed when enclosed within double quotes. The variable name is expanded and the command in backquotes, date, is executed.

10.1.15 Command Substitution

Command substitution is used when assigning the output of a command to a variable, or when substituting the output of a command into a string. The Bourne and C shells use backquotes to perform command substitution. The Korn shell does allow the backquote format (calling it "obsolescent"), but placing the command in parentheses is the preferred method because it has simpler quoting rules and makes nesting commands easier.

FORMAT

'Unix command'      # Old method with backquotes $(Unix command)     # New method 
Example 10.55
(Old Way) 1   $ print "The hour is 'date +%H'"     The hour is 09 2   $ name='nawk  F: ''{print $1}' database'     $ print $name     Ebenezer Scrooge 3   $ ls 'ls /etc'     shutdown 4   $ set 'date' 5   $ print $*     Sat Oct 13 09:35:21 PDT 2001 6   $ print $2 $6     Oct 2001 

EXPLANATION

  1. The output of the date command is substituted into the string.

  2. The output of the nawk command is assigned to the variable name, and displayed.

  3. The output of the ls command, enclosed in backquotes, is a list of files from the /etc directory. The filenames will be arguments to the first ls command. All files with the same name in /etc as are in the current directory are listed.

  4. The set command assigns the output of the date command to positional parameters. Whitespace separates the list of words into its respective parameters.

  5. The $* variable holds all of the parameters. The output of the date command was stored in the $* variable. Each parameter is separated by whitespace.

  6. The second and sixth parameters are printed.

The ksh alternate for using backquotes in command substitution is presented in Example 10.56.

Example 10.56
(The New ksh Way) 1   $ d=$(date)     print $d     Sat Oct 20 09:35:21 PDT 2001 2   $ line = $(< filex) 3   $ print The time is $(date +%H)     The time is 09 4   $ machine=$(uname  n)     $ print $machine     jody 5   $ dirname="$(basename $(pwd)) "      # Nesting commands     $ print $dirname     bin 

EXPLANATION

  1. The date command is enclosed within parentheses. The output of the command is returned to the expression, assigned to the variable d, and displayed.

  2. The input from the file is assigned to the variable line. The < filex notation has the same effect as 'cat filex'. Command substitution is performed within the parentheses when the parentheses are preceded with a dollar sign.

  3. The UNIX date command and its hour argument, +%H, are enclosed within parentheses. Command substitution is performed, and the results are placed within the print string.

  4. Command substitution has been performed. The output of the UNIX uname command is assigned to the variable machine.

  5. To set the variable dirname to the name (only) of the present working directory, command substitution is nested. The pwd command is executed first, passing the full pathname of the present working directory as an argument to the UNIX command basename. The basename command strips off all but the last element of a pathname. Nesting commands within backquotes is not allowed.

10.1.16 Functions

This section introduces functions so that you can use them interactively or store them in your initialization files. Later, when discussing scripts, functions will be covered in more depth. Functions can be used when an alias is not enough, that is, for passing arguments. Functions are often defined in the user's initialization file, .profile. They are like mini-scripts, but unlike scripts, functions run in the current environment; that is, the shell does not fork a child process to execute a function. All variables are shared with the shell that invoked the function. Often functions are used to improve the modularity of a script. Once defined, they can be used repeatedly and even stored in another directory.

Functions must be defined before they are invoked; there are two formats used to define them. One format came from the Bourne shell and the other is new with the Korn shell. Functions can be exported from one invocation of the shell to the next. The typeset function and unset command can be used to list and unset functions. See Table 10.17.

Table 10.17. The typeset Command and Functions
Command Function
typeset f Lists functions and their definitions; functions is an alias for typeset f.
typeset +f Lists only function names.
unset f name Unset a function.

Defining Functions. There are two acceptable formats for defining functions: the Bourne shell format (still allowed for upward compatibility) and the new Korn shell format. A function must be defined before it can be used.

FORMAT

(Bourne Shell) functionname() { commands ; commands; }[5] (Korn Shell) function functionname { commands; commands; } 
Example 10.57
1   $ function fun { pwd; ls; date; } 2   $ fun     /home/jody/ellie/prac     abc      abc123   file1.bak  none      nothing  tmp     abc1     abc2     file2      nonsense  nowhere  touch     abc122   file1    file2.bak  noone     one     Tue Feb 9 11:15:48 PST 2001 3   $ function greet { print "Hi $1 and $2"; } 4    greet tom joe              # Here $1 is tom and $2 is joe     Hi tom and joe 5   $ set jane nina lizzy 6   $ print $*     jane nina lizzy 7   $ greet tom joe     Hi tom and joe 8   $ print $1 $2     jane nina 

EXPLANATION

  1. The function fun is named and defined. The name is followed by a list of commands enclosed in curly braces. Each command is separated by a semicolon. There must be a space after the first curly brace or you will get a syntax error such as ksh: syntax error: `}' unexpected. A function must be defined before it can be used.

  2. The function behaves like a script or an alias when invoked. Each of the commands in the function definition is executed in turn.

  3. There are two positional parameters used in the function greet. When arguments are given to the function, the positional parameters are assigned those values.

  4. The arguments to the function tom and joe are assigned to $1 and $2, respectively. The positional parameters in a function are private to the function and will not interfere with any used outside the function.

  5. The positional parameters are set at the command line. These variables have nothing to do with the ones set in the function.

  6. $* displays the values of the currently set positional parameters.

  7. The function greet is called. The values assigned to the positional parameters $1 and $2 are tom and joe, respectively.

  8. The positional variables assigned at the command line are unaffected by those set in the function.

Functions and Aliases. When processing the command line, the shell looks for aliases before special built-in commands and for special built-ins before functions. If a function has the same name as a built-in, the built-in will take priority over the function. An alias for a special built-in can be defined, and then the function name can be given the name of the alias to override the order of processing.

Example 10.58
(The ENV File) 1   alias cd=_cd 2   function _cd { 3   \cd $1 4   print $(basename $PWD) 5   } (The Command Line) $ cd / / $ cd $HOME/bin bin $ cd .. ellie 

EXPLANATION

  1. The alias for cd is assigned _cd.

  2. The function _cd is defined. The opening curly brace marks the start of the function definition.

  3. If an alias is preceded by a backslash, alias substitution is not performed. The backslash precedes cd to execute the built-in cd command, not the alias. Without the backslash, the function would be recursive and the shell would display an error message: cd_: recursion too deep. $1 is the argument (name of a directory ) passed to cd.

  4. The name of the directory (not the full pathname) is printed.

  5. The closing curly brace marks the end of the function definition.

Listing Functions. To list functions and their definitions, use the typeset command.

Example 10.59
(The Command Line) 1   $ typeset  f     function fun     {     pwd; ls; date; }     function greet     {     print "hi $1 and $2"; } 2   $ typeset +f     fun     greet 

EXPLANATION

  1. The typeset command, with the f option, lists the function and its definition.

  2. The typeset command, with the +f option, lists only the names of defined functions.

Unsetting Functions. When a function is unset, it will be removed from the shell's memory.

Example 10.60
(The Command Line) 1   $ typeset  f     function fun     {         pwd; ls; date; }     function greet     {     print "hi $1 and $2"; } 2   $ unset -f fun 3   $ typeset  f     function greet     {     print "hi $1 and $2"; } 

EXPLANATION

  1. The typeset f command displays the function and its definition. Two functions, fun and greet, are displayed.

  2. The built-in command unset, with the f option, undefines the fun function, removing it from the shell's memory.

  3. The fun function is no longer shown as one of the functions defined when the typeset f command is executed.

10.1.17 Standard I/O and Redirection

The shell opens three files (called streams) whenever a program is started: stdin, stdout, and stderr. Standard input normally comes from the keyboard and is associated with file descriptor 0. Standard output normally goes to the screen, file descriptor 1. Standard error normally goes to the screen, file descriptor 2. Standard input, output, and error can be redirected to or from a file. See Table 10.18 for a list of redirection operators.

Table 10.18. Redirection
Operator Function
< Redirect input.
> Redirect output.
>> Append output.
2> Redirect error.
1>&2 Redirect output to where error is going.
2>&1 Redirect error to where output is going.
Example 10.61
(The Command Line) 1   $ tr '[A-Z]' '[a-z]' < myfile   # Redirect input 2   $ ls > lsfile         # Redirect output     $ cat lsfile     dir1     dir2     file1     file2     file3 3   $ date >> lsfile      # Redirect and append output     $ cat lsfile     dir1     dir2     file1     file2     file3     Mon Sept 17 12:57:22 PDT 2001 4   $ cc prog.c 2> errfile           # Redirect error 5   $ find .  name \*.c  print > founditfile 2> /dev/null 6   $ find .  name \*.c  print > foundit 2>&1 7   $ print "File needs an argument" 1>&2 8   $ function usage { print "Usage: $0 [-y] [-g] filename" 1>&2 ;            exit 1; } 

EXPLANATION

  1. The standard input is redirected from the file myfile to the UNIX tr command. All uppercase letters are converted to lowercase letters.

  2. The ls command redirects its output to the file lsfile.

  3. The output of the date command is redirected and appended to lsfile.

  4. The file prog.c is compiled. If the compile fails, standard error is redirected to errfile.

  5. The find command starts searching in the current working directory for filenames ending in .c and prints the files to a filenamed founditfile. Errors from the find command are sent to /dev/null.

  6. The find command starts searching in the current working directory for filenames ending in .c and prints the files to a filenamed foundit. The standard error (file descriptor 2) is being sent to the same place that the standard output (file descriptor 1) is being sent, to the file called foundit.

  7. The print command sends its message to standard error. Standard output is merged with standard error; that is, standard output is being redirected to the place where standard error goes, the terminal. This makes it possible to separate error messages from "good" output.

  8. The function usage is defined. This function, when called, will print a usage message, send the output to standard error, and exit. This type of function is often used in scripts.

The exec Command and Redirection. The exec command can be used to replace the current program with the one being execed. Another use for the exec command is to change standard output or input without creating a subshell. If a file is opened with exec, subsequent read commands will move the file pointer down the file a line at a time until end of file. The file must be closed to start reading from the beginning again. However, if using UNIX utilities such as cat and sort, the operating system closes the file after each command has completed. See Table 10.19 for exec functionality.

Table 10.19. exec Commands
Command Function
exec ls ls will execute in place of the shell. When ls is finished, the shell in which it was started does not return.
exec < filea Open filea for reading standard input.
exec > filex Open filex for writing standard output.
exec 2> errors Open errors for writing standard error.
exec 2> /dev/console Sends all error messages to the console.
exec 3< datfile Open datfile as file descriptor 3 for reading input.
sort <&3 datfile is sorted.
exec 4>newfile Open newfile as file descriptor 4 for writing.
ls >&4 Output of ls is redirected to newfile.
exec 5<&4 Make fd 5 a copy of fd 4. Both descriptors refer to newfile.
exec 3<& Close file descriptor 3, datfile.

10.1.18 Redirection and the Child Shell

When the output of a command is redirected from the screen to a file, the Korn shell creates a child (fork) shell to rearrange the file descriptors, as shown in Figure 10.2.

Figure 10.2. Redirection of standard output and errors.

graphics/10fig02.gif

10.1.19 Pipes

A pipe takes the output from the command on the left-hand side of the pipe symbol and sends it to the input of a command on the right-hand side of the pipe symbol. A pipeline can consist of more than one pipe.

Example 10.62
1   $ who > tmp 2   $ wc  l tmp     4 tmp 3   $ rm tmp 4   $ who | wc -l      # Using the pipe 

EXPLANATION

The purpose of lines 1 through 3 is to count the number of people logged on (who), save the output of the command in a file (tmp), use the wc l to count the number of lines in the tmp file (wc l), and then remove the tmp file; that is, find the number of people logged on. The pipe performs the same task in one command.

  1. The output of the who command is redirected to the tmp file.

  2. The wc l command displays the number of lines in tmp.

  3. The tmp file is removed.

  4. With the pipe facility, you can perform all three of the above steps, 1,2, and 3, in one step. The output of the who command is sent to an anonymous kernel buffer (instead of to a temporary file that requires disk space); the wc l command reads from the buffer and sends its output to the screen.

Example 10.63
1   $ ls | more     < lists (ls) all files one page at a time (more) > 2   $ du ~ | sort  n | sed  n '$p'     72388  /home/jody/ellie 3   $ cat | lp or  cat | lpr 

EXPLANATION

  1. The ls output is piped to the more command, which accepts input. Output is displayed one page at a time.

  2. The output of the du command (disk usage) is sorted numerically and piped to the sed command (stream editor), which displays only the last line ($p).

  3. The cat command reads from standard input; its output is piped to the line printer (lp in SVR4 and lpr in BSD).

10.1.20 The here document and Redirecting Input

A here document captures in-line input for programs such as mail, sort, and cat. Input is placed between two words or symbols. The first word is preceded by a UNIX command and the << symbol. The next line(s) consist of the input to be received by the command. The last line consists of a second word that exactly matches the first word. This word is called the final terminator and marks the end of input. It is used in the same way Control-D is used to terminate input. There can be no spaces surrounding the final terminator. If the first word is preceded by the << , leading tabs (and only tabs) may precede the final terminator. Normally, here documents are used in shell scripts, rather than interactively. A good use for a here document is to create a menu in a script.

FORMAT

UNIX command << TERMINATOR    lines of input    input TERMINATOR 
Example 10.64
(The Command Line) 1   $ cat << FINISH# FINISH is a user-defined terminator 2   > Hello there $LOGNAME 3   > The time is $(date)     > I can't wait to see you!!! 4   > FINISH 5   Hello there ellie     The time is Sun Feb 7 19:42:16 PST 2001     I can't wait to see you!! 6   $ 

EXPLANATION

  1. The UNIX cat program will accept input until the word FINISH appears on a line by itself.

  2. Variable substitution is performed within the here document. The > is the Korn shell's secondary prompt.

  3. Command substitution is performed within the here document.

  4. The user-defined terminator, FINISH, marks the end of input for the cat program. It cannot have any spaces before or after it and is on a line by itself.

  5. The output from the cat program is displayed.

  6. The shell prompt reappears.

Example 10.65
(From the .profile File) 1   print "Select a terminal type" 2   cat << EOF       [1] sun       [2] ansi       [3] wyse50 3   EOF 4   read TERM     ... 

EXPLANATION

  1. The user is asked to select a terminal type.

  2. The menu will appear on the screen. This is a here document, meaing from here until the matching EOF on line 3 is reached, input will be given to the cat command. You could use a series of echo commands to get the same results, but visually, the here document is nicer.

  3. EOF is a user-defined terminator, marking the end of the here document. It must be at the left margin with no spaces surrounding it.

  4. The user input will be read in from the keyboard and assigned to TERM.

Example 10.66
(The Command Line) 1   $ cat <<  DONE     >Hello there     >What's up?     >Bye now The time is $(date). 2   >    DONE 3   Hello there     What's up?     Bye now The time is Sun Feb 7 19:48:23 PST 2001. 

EXPLANATION

  1. The cat program accepts input until DONE appears on a line by itself. The << operator allows the final terminator to be preceded by one or more tabs. (The > is the shell's secondary prompt.)

  2. The final matching DONE terminator is preceded by a tab. From the first DONE on line 1 to the last DONE on this line, the text in between is sent as input to the cat command.

  3. The output of the cat program is displayed on the screen.

10.1.21 Timing Commands

The time Command. The time command is a ksh built-in command. The time command prints the following to standard error: elapsed time, the user time, and the system time used to execute a command.

Example 10.67
1   $ time sleep 3     real  0m3.15s   took 3.15 seconds to run     user  0m0.01s   sleep used its own code for .01 seconds     sys   0m0.08s   and kernel code for .08 seconds 2   $ time ps  ef | wc  l    # time is measured for all commands in     38                       # the pipeline     real  0m1.03s     user  0m0.01s     sys   0m0.10s 

EXPLANATION

  1. The time command will display the total amount of time elapsed to run the command, the time the user part of the program took to run, and the time the kernel spent running the program. The sleep command took 3.15 seconds to run.

  2. The time is measured for the ps command and wc command.

10.1.22 The TMOUT Variable

The TMOUT variable is an integer type. It can be set to force users to type commands within a certain period of time. TMOUT, by default, is set to zero, allowing the user an infinite amount of time to type commands after the PS1 prompt. If TMOUT is set to a value greater than zero, the shell will terminate after the time has expired. Sixty additional seconds will be allotted as the grace period before actually exiting the shell.

Example 10.68
$ TMOUT=600 time out in 60 seconds due to inactivity ksh: timed out waiting for input 

EXPLANATION

The TMOUT variable is set to 600 seconds. If the user does nothing for 600 seconds, a message will appear on the screen and then an additional 60 seconds grace period will be allotted before the shell exits. If you do this at the prompt, your current shell exits.

10.2 Programming with the Korn Shell

Writing shell scripts requires a few steps, as outlined in the following section.

10.2.1 The Steps in Creating a Shell Script

A shell script is normally written in an editor and consists of commands interspersed with comments. Comments are preceded by a pound sign.

The First Line. At the top-left corner, to indicate the program that will be executing the lines in the script, #!/bin/ksh is commonly used. The #! is called a magic number and is used by the kernel to identify the program that should be interpreting the lines in the script. This line must be the top line of your script. The Korn shell also provides a number of invocation options that control how the shell behaves. These options are listed at the end of this chapter.

Comments. Comments are lines preceded by a pound sign. They are used to document your script. It is sometimes difficult to understand what the script is supposed to do if it is not commented. Although comments are important, they are often too sparse or not even used at all. Try to get used to commenting what you are doing, not only for someone else, but also for yourself.

Executable Statements and Korn Shell Constructs. A Korn shell program consists of a combination of UNIX commands, Korn shell commands, programming constructs, and comments.

Naming and Storing Scripts. When naming scripts, it is a good idea to give the script a meaningful name and one that does not conflict with other UNIX commands or aliases. For example, you may want to call the script test because it is merely performing some simple test procedure, but test is a built-in command and you may find you are executing the wrong test. Additionally, if you name the file foo, goo, boobar, and so forth, in a few days or even hours you may not have any idea what is in that script!

After you have tested your script and found it "bug-free," make a directory where you can store the scripts, then set the path so that your scripts can be executed from anywhere in the directory hierarchy.

Example 10.69
1   $ mkdir ~/bin 2   $ mv myscript ~/bin (In .profile) 3   export PATH=${PATH}:~/bin 4   $ . .profile 

EXPLANATION

  1. A common place to store scripts is in a directory under your home directory called bin.

  2. The script, called myscript, is moved into the new bin directory.

  3. The new directory is added to the PATH variable in the .profile initialization file.

  4. The dot command causes the .profile file to be executed in the current environment so that you do not have to log out and then back in to enable the new setting.

Making a Script Executable. When you create a file, it is not automatically given execute permission (regardless of how umask is set). You need this permission to run your script. Use the chmod command to turn on execute permission.

Example 10.70
1   $ chmod +x myscript 2   $ ls -lF myscript     -rwxr--xr--x  1 ellie      0 Jul 12 13:00 joker* 

EXPLANATION

  1. The chmod command is used to turn on execute permission for the user, group, and others.

  2. The output of the ls command indicates that all users have execute permission on the joker file. The asterisk at the end of the filename also indicates that this is an executable program.

Using a Script as an Argument to ksh. If you don't make a script executable, you can execute it by passing it as an argument to the ksh command:

Example 10.71
(The Command Line) $ ksh myscript 

EXPLANATION

If the ksh program is given a scriptname as its argument, it will execute the script and the #! line is not necessary or even used.

A Scripting Session. In Example 10.72, the user will create a script in the editor. After saving the file, the execute permissions are turned on, and the script is executed. If there are errors in the program, the Korn shell will respond immediately.

Example 10.72
(The Script) 1   #!/bin/ksh 2   # This is the first Korn shell program of the day.     # Scriptname: greetings     # Written by: Karen Korny 3   print "Hello $LOGNAME, it's nice talking to you." 4   print "Your present working directory is $(pwd)."     print "You are working on a machine called $(uname -n)."     print "Here is a list of your files." 5   ls     # List files in the present working directory     print "Bye for now $LOGNAME. The time is $(date +%T)!" (The Command Line)     $ chmod +x greetings     $ greetings 3   Hello karen, it's nice talking to you. 4   Your present working directory is /home/lion/karen/junk     Your are working on a machine called lion.     Here is a list of your files. 5   Afile        cplus  letter    prac     Answerbook  cprog   library   prac1     bourne       joke   notes     perl5     Bye for now karen. The time is 18:05:07! 

EXPLANATION

  1. The first line of the script, #!/bin/ksh, lets the kernel know what interpreter will execute the lines in this program.

  2. The comments are nonexecutable lines preceded by a #. They can be on a line by themselves or inserted in a line after a command.

  3. The print command displays the line on the screen, after variable substitution is performed by the shell.

  4. The print command displays the line on the screen, after command substitution is performed by the shell.

  5. The ls command is executed. The comment, any text on the line after the pound sign (#), will be ignored by the shell.

10.2.2 Reading User Input

The read command is used to take input from the terminal or from a file until the newline is reached. The Korn shell provides some additional options for the read command. See Table 10.20 for different read formats. See Table 10.21 for read options.

Table 10.20. The read Format
Format Meaning
read answer Reads a line from standard input and assigns it to the variable answer.
read first last Reads a line from standard input to the first whitespace or newline, putting the first word typed into the variable first and the rest of the line into the variable last.
read response?"Do you feel okay?" Displays the string Do you feel okay? to standard error and waits for user to type a reply, then puts the reply in the variable response. This form of read requires and accepts only one variable. Whatever the user types, until the newline, will be stored in response.
read u3 line Reads a line from file descriptor 3 into variable line.
read Reads input into a built-in variable, REPLY.

 

Table 10.21. The read Options
Options Meaning
r Treats newline character, the \n, as a literal.
s Copies a line into the history file.
un Reads from file descriptor n; the default is fd 0, or standard input.
p Reads a line of input from a coprocess.
On Versions of ksh Newer than 1988:
A Stores the fields as an array, index starting at zero.
t sec Puts a limit of seconds on the user's response time.
d char Used as an alternate delimiter for terminating input; newline is the default.
Example 10.73
(The Script)     #!/bin/ksh     # Scriptname: nosy     print  n "Are you happy? " 1   read answer     print "$answer is the right response."     print  n "What is your full name? " 2   read first middle last     print "Hello $first"     print  n "Where do you work? " 3   read 4   print I guess $REPLY keeps you busy! 5   read place?"Where do you live? "     # New ksh read and print combined     print Welcome to $place, $first $last (The Output)     $ nosy     Are you happy? Yes 1   Yes is the right response. 2   What is your full name? Jon Jake Jones     Hello Jon 3   Where do you work? Tandem 4   I guess Tandem keeps you busy! 5   Where do you live? Timbuktu     Welcome to Timbuktu, Jon Jones 

EXPLANATION

  1. The read command accepts a line of user input and assigns the input to the variable answer.

  2. The read command accepts input from the user and assigns the first word of input to the variable first, assigns the second word of input to the variable middle, and all the rest of the words to the end of the line to the variable last.

  3. The read command, without an argument, accepts a line of input from the user and assigns the input to the built-in variable REPLY.

  4. After the shell has performed variable substitution, the print function prints the string, showing the value of the built-in REPLY variable.

  5. If the variable following the read command is appended with a question mark (?), the string after the question mark is displayed as a prompt. The user input is stored in the variable place.

Read and File Descriptors. When the system boots up, three files called streams (stdin, stdout, and stderr) are opened and assigned to an array of file descriptors. The first three file descriptors, 0, 1, and 2, are for standard input, standard output, and standard error, respectively. The next file descriptor available is file descriptor 3. The u option allows the read command to read directly from the file descriptor.

Example 10.74
(The Command Line) 1   $ cat filex     Captain Kidd     Scarlett O'Hara 2   $ exec 3< filex     # filex is assigned to file descriptor 3 for reading 3   $ read  u3 name1     # read from filex and store input in variable, name1 4   $ print $name1     Captain Kidd 5   $ read  u3 name2     $ print $name2     Scarlett O'Hara 6   $ exec 3<&-               # Close file descriptor 3 7   $ read -u3 line     ksh: read: bad file unit number 

EXPLANATION

  1. The contents of filex are displayed.

  2. The exec command is used to open file descriptor 3 for reading from filex.

  3. The read command reads one line directly from unit 3 (file descriptor 3, filex) and assigns that line to the variable name1.

  4. The line stored in name1 is printed.

  5. The file filex is still open, and this read command reads the next line from the file and stores that line in the variable name2.

  6. File descriptor 3 (unit 3) is closed. filex is no longer open.

  7. Since file descriptor 3 (filex) has been closed, the read command fails when attempting to read input from that descriptor into variable line.

Reading through Files. Example 10.75 uses the read command with a while loop. The loop will iterate through the file one line at a time. When end of file is reached, the loop terminates. The files are opened with descriptors (units) for reading.

Example 10.75
(The Files) 1   $ cat names     Merry Melody     Nancy Drew     Rex Allen     $ cat addresses     150 Piano Place     5 Mystery Lane     130 Cowboy Terrace ------------------------------------------------------------------ (The Script)     #!/bin/ksh     # Scriptname: readit 2   while read -u3 line1 && read -u4 line2     do 3      print "$line1:$line2" 4   done 3<$1 4<$2 ------------------------------------------------------------------ (The Command Line) 5   $ readit names addresses     Merry Melody:150 Piano Place     Nancy Drew:5 Mystery Lane     Rex Allen:130 Cowboy Terrace --------------------------------------------------------------- 

EXPLANATION

  1. The contents of two files, names and addresses, are displayed.

  2. The while loop is started. The read command reads a line of input from file descriptor 3 (unit 3) and, if successful, reads another line from file descriptor 4. The file descriptors (units) are assigned filenames on line 4. The filenames are being passed as arguments, or positional parameters 1 and 2.

  3. The value of the first variable, a colon, and the value of the second variable are displayed.

  4. The input assigned to file descriptor 3 is the first command line argument, names. The input assigned to file descriptor 4 is the second command line argument, addresses.

  5. The script is executed with command line arguments (the names of two files).

10.2.3 Arithmetic

The Korn shell supports both integer and floating point arithmetic, but floating point arithmetic is available only on versions of the Korn shell newer than 1988. The typeset command is used for assigning types. See Table 10.22 for the typeset command.

Table 10.22. typeset and Arithmetic
typeset Command Alias Meaning
typeset i variable integer variable Variable is only allowed integer assignment.
typeset i#   # is the base number for the integer.
On Versions of ksh Newer than 1988:
typeset F variable   Floating point number assignment.
typeset E variable float variable Floating point number assignment.

The Integer Type. Variables can be declared as integers with the typeset i command or its alias, integer. If you attempt to assign any string value, ksh returns an error. If you assign a floating point number, the decimal point and the fractional value will be truncated. The integer alias can be used instead of typeset i. Numbers can also be represented in different bases such as binary, octal, and hex.

Example 10.76
1   $ typeset  i num or integer num       # integer is an alias for                                           # typeset -i 2   $ num=hello     /bin/ksh: hello: bad number 3   $ num=5 + 5     /bin/ksh: +: not found 4   $ num=5+5     $ echo $num     10 5   $ num=4*6     $ echo $num     24 6   $ num="4 * 6"     $ echo $num     24 7   $ num=6.789     $ echo $num     6 

EXPLANATION

  1. The typeset command with the i option creates an integer variable num.

  2. Trying to assign the string hello to the integer variable num causes an error.

  3. The whitespace must be quoted or removed unless the (( )) operators are used (see "Arithmetic Operators and the let Command" on page 511).

  4. The whitespace is removed and arithmetic is performed.

  5. Multiplication is performed and the result assigned to num.

  6. The whitespace is quoted so that the multiplication can be performed and to keep the shell from expanding the wildcard (*).

  7. Since the variable is set to integer, the fractional part of the number is truncated.

Using Different Bases. Numbers can be represented in decimal (base 10), octal (base 8), and so forth, by using the typeset command and with the i option and the base number.[6]

Example 10.77
1   $ num=15 2   $ typeset  i2 num    # binary     $ print $num     2#1111 3   $ typeset  i8 num    # octal     $ print $num     8#17 4   $ typeset  i16 num   # hex     $ print $num     16#f 5   $ read number     2#1101     $ print $number     2#1101 6   $ typeset  i number     $ print $number     2#1101 7   $ typeset  i10 number   # decimal     $ print $number     13 8   $ typeset  i8 number    # octal     $ print $number     8#15 

EXPLANATION

  1. The variable num is assigned the value 15.

  2. The typeset command converts the number to a binary format. The display is the base of the number (2), followed by a pound sign (#), and the value of the number in binary.

  3. The typeset command converts the number to an octal format and displays the value of the number in base 8.

  4. The typeset command converts the number to hexadecimal format and displays the value of the number in base 16.

  5. The read command accepts input from the user. The input is entered in binary format, stored in the variable number, and displayed in binary format.

  6. The typeset command converts number to an integer. It still displays in binary format.

  7. The typeset command converts number to a decimal integer and displays it.

  8. The typeset command converts number to octal and displays its value in base 8.

Listing Integers. The typeset command with only the i argument will list all preset integers and their values, as shown in the following display.

$ typeset  i ERRNO=2 LINENO=1 MAILCHECK=600 OPTIND=1 PPID=4881 RANDOM=25022 SECONDS=47366 TMOUT=0 n=5 number=#15 

Arithmetic Operators and the let Command. The let command is a Korn shell built-in command that is used to perform integer arithmetic. This replaces the Bourne shell integer testing. The alternative and preferred way to use the let command is with the (( )) operator.

Table 10.23. let Operators
Operator Meaning
Unary minus.
! Logical not.
~ Bitwise not.
* / % Multiply, divide, remainder.
+ Add, subtract.
<< >> Bitwise left shift, right shift.
<= >= < > == != Comparison operators.
& ^ | Bitwise and; exclusive or.
&& || ! Logical and; logical or; unary not.
= Assignment.
*= /= %= += = <<= >>= &= ^= |= Shortcut assignment.

Note

The ++ and operators are supported on versions of ksh that are newer than 1988.

Example 10.78
1   $ i=5 2   $ let i=i+1     $ print $i     6 3   $ let "i = i + 2"     $ print $i     8 4   $ let "i+=1"     $ print $i     9 

EXPLANATION

  1. The variable i is assigned the value 5.

  2. The let command will add 1 to the value of i. The $ (dollar sign) is not required for variable substitution when performing arithmetic.

  3. The quotes are needed if the arguments contain whitespace.

  4. The shortcut operator, +=, is used to add 1 to the value of i.

Example 10.79
(The Command Line) 1   $ (( i = 9 )) 2   $ (( i = i * 6 ))     $ print $i     54 3   $ (( i > 0 && i <= 10 )) 4   $ print $?     1     $ j=100 5   $ (( i < j || i == 5 )) 6   $ print $?     0 7   $ if (( i < j && i == 54 ))     > then     > print True     >fi     True     $ 

EXPLANATION

  1. The variable i is assigned the value 9. The (( )) operators are an alternate form of the let command. Since the expression is enclosed in double parentheses, spaces are allowed between the operators.

  2. The variable i is assigned the product of i*6.

  3. The numeric expressions are tested. If both expressions are true, zero exit status is returned.

  4. The special ? variable holds the exit status of the last command (the let command) executed. Since the value is one, the command failed (evaluated as false).

  5. The numeric expressions are tested. If one of the expressions is true, zero exit status is returned.

  6. The special ? variable holds the exit status of the last command (the let command) executed. Since the value is zero, the command succeeded (evaluated as true).

  7. The if conditional command precedes the let command. The secondary prompt appears while waiting for the command to be completed. If the exit status is zero, the commands after the then statement are executed; otherwise, the primary prompt returns.

10.2.4 Positional Parameters and Command Line Arguments

Command line arguments can be referenced in scripts with positional parameters; for example, $1 is set to the first argument, $2 to the second argument, and $3 to the third argument. Positional parameters can be reset with the set command. See Table 10.24.

Table 10.24. Positional Parameters
Variable Function
$0 References the name of the script.
$# Holds the value of the number of positional parameters.
$* Contains a list of all the positional parameters.
$@ Means the same as $*, except when enclosed in double quotes.
"$*" Expands to a single argument, e.g., "$1 $2 $3".
"$@" Expands to separate arguments, e.g., "$1" "$2" "$3".

The set Command and Positional Parameters. The set command sets the positional parameters. If the positional parameters have already been set, the set command will reset them, removing any values in the old list. To unset all of the positional parameters, use set .

Example 10.80
    (The Script)     $ cat args     #!/bin/ksh     # Script to test command line arguments 1   print The name of this script is $0. 2   print The arguments are $*. 3   print The first argument is $1. 4   print The second argument is $2. 5   print The number of arguments is $#. 6   oldparameters=$* 7   set Jake Nicky Scott 8   print All the positional parameters are $*. 9   print The number of positional parameters is $#. 10  print $oldparameters 11  set -- 12  print Good bye for now, $1. 13  set $oldparameters 14  print $*     (The Output)     $ args a b c d 1   The name of this script is args. 2   The arguments are a b c d. 3   The first argument is a. 4   The second argument is b. 5   The number of arguments is 4. 8   All the positional parameters are Jake Nicky Scott. 9   The number of positional parameters is 3. 10  a b c d 12  Good-bye for now ,. 14  a b c d     $ 

EXPLANATION

  1. The name of the script is stored in the $0 variable.

  2. $* (and $@) both represent all of the positional parameters.

  3. $1 represents the first positional parameter (command line argument).

  4. $2 represents the second positional parameter.

  5. $# is the total number of positional parameters (command line arguments).

  6. The variable oldparameters is assigned all of the positional parameters ($*). Later on, if you want to get back your original parameters, you can do so by typing set $oldparameters.

  7. Reset positional parameters with the set command. The set command completely clears all previously set parameters. Jake is assigned to $1, Nicky is assigned to $2, and Scott is assigned to $3.

  8. The new positional parameters are printed.

  9. The number of positional parameters is printed.

  10. The original parameters were stored in the variable oldparameters. They are printed.

  11. All parameters are unassigned.

  12. $1 has no value. The parameters list was cleared with the set command.

  13. A new parameter list is assigned by substituting the values in oldparameters to the parameter list with the set command.

  14. All the positional parameters are printed.

Example 10.81
(How $* and $@ Differ) 1   $ set 'apple pie' pears peaches 2   $ for i in $*     > do     > echo $i     > done     apple     pie     pears     peaches 3   $ set 'apple pie' pears peaches 4   $ for i in "$*"     > do     > echo $i     > done     apple pie pears peaches 5   $ set 'apple pie' pears peaches 6   $ for i in $@     > do     > echo $i     > done     apple     pie     pears     peaches 7   $ set 'apple pie' pears peaches 8   $ for i in "$@"            # At last!!     > do     > echo $i     > done     apple pie     pears     peaches 

EXPLANATION

  1. The positional parameters are set. When the $* is expanded, the quotes are stripped and apple pie becomes two separate words. The for loop assigns each of the words, in turn, to the variable i and then prints the value of i. Each time through the loop, the word on the left is shifted off, and the next word is assigned to i.

  2. If $* is surrounded by double quotes, all of the words in the list become one single string, and the whole string is assigned to the variable i.

  3. The positional parameters are set.

  4. By enclosing $* in double quotes, the entire parameter list becomes one string.

  5. The positional parameters are set.

  6. Unquoted, the $@ behaves the same way as the $*.

  7. The positional parameters are set.

  8. By surrounding $@ with double quotes, each of the positional parameters is treated as a quoted string. The list would consist of "apple pie," "pears," and "peaches." Each of the quoted words is assigned to i, in turn, as the loop goes through each iteration.

10.2.5 Testing Exit Status and the $? Variable

The ? variable contains a number value (between 0 and 255) representing the exit status of the last command that exited. If the exit status is zero, the command exited with success; if nonzero, the command failed in some way. You can test the exit status of commands and use the test command to test the exit status of expressions.

The following examples illustrate how the exit status is tested. The single brackets are used in the Bourne shell, and although perfectly acceptable in the Korn shell, Dr. Korn provides you with the new double-bracket notation for testing expressions.

Example 10.82
    (The Command Line) 1   $ name=Tom 2   $ grep "$name" datafile     Tom Savage:408-124-2345 3   $ print $?     0                         # Success! 4   $ test $name = Tom 5   $ print $?     0                         # Success 6   $ test $name != Tom     $ print $?     1                         # Failure 7   $ [ $name = Tom ]         # Brackets instead of the test command 8   $ print $?     0 9   $ [[ $name = [Tt]?m ]]    # New ksh test command 10  $ print $?     0 

EXPLANATION

  1. The string Tom is assigned to the variable name.

  2. The grep command will search for string Tom in the datafile, and if successful in its search, will display the line found.

  3. The ? variable, accessed by $?, contains the exit status of the last command executed, in this case, the exit status of grep. If grep is successful in finding the string Tom, it will return an exit status of zero. The grep command was successful.

  4. The test command is used to evaluate strings and numbers, and to perform file testing. It returns an exit status of zero if the expression is true, and an exit status of one if the expression fails. There must be spaces surrounding the equal sign.

  5. The value of name is tested to see if it is equal to Tom. The test command returns an exit status of 0, meaning that $name does evaluate to Tom.

  6. The value of name is tested to see if it is equal to Tom. The test command returns an exit status of 1, meaning that name is not equal to Tom.

  7. The brackets are an alternate notation for the test command. There must be spaces after the first bracket. The expression is tested to see if $name evaluates to the string Tom.

  8. The exit status of the test is zero. The test was successful because $name is equal to Tom.

  9. The new Korn shell test command, [[, is used. The new test allows shell metacharacter expansion. If the variable matches Tom, tom, Tim, tim, and so on, the test will return a successful status, zero.

  10. The variable name did match a string beginning with T or t and ending in m, resulting in a successful exit status ($?) of 0.

10.2.6 Conditional Constructs and Flow Control

Conditional commands allow you to perform some task(s) based on whether or not a condition succeeds or fails. The if command is the simplest form of decision making. The if/else commands allow a two-way decision construct, and the if/elif/else commands allow a multiway decision construct.

The Korn shell expects a command to follow an if. The command can be a system command or a built-in command. The exit status of the command is used to evaluate the condition. To evaluate an expression, the built-in test command is used. This command is also linked to the [ and the [[ symbols. The Bourne shell encloses an expression in a set of single brackets: [ and ]. The Korn shell has a more sophisticated method for testing expressions. The expression is enclosed in double brackets: [[ and ]]. In the single brackets, the expansion of wildcards is not allowed; with the double brackets (Korn shell only), wildcard expansion is supported and a new set of operators have been added. The result of a command is tested, with zero status indicating success, and nonzero status indicating failure.

The Old test Command. The test command is used to evaluate conditional expressions, returning true or false. It returns zero exit status for true, and nonzero exit status for false. Either the test command or the brackets can be used. The Korn shell introduced a new way of testing expressions with double brackets. For backward-compatibility with the Bourne shell, the older form of test can be used with either the test command or the single brackets. However, the preferred method for Korn shell programmers is the new test with double brackets. A complete list to test operators (both old and new style) are listed in Table 10.25.

Table 10.25. Testing and Logical Operators
test Tests For
String Testing:
string1 = string2 string1 is equal to string2.
string1 != string2 string1 is not equal to string2.
string string is not null.
z string length of string is zero.
n string length of string is nonzero.
 

Examples:

test n $word or [ n $word ]

test tom = sue or [ tom = sue ]

Integer Testing (Old-Style test Used with Bourne Shell):
int1 eq int2 int1 is equal to int2.
int1 ne int2 int1 is not equal to int2.
int1 gt int2 int1 is greater than int2.
int1 ge int2 int1 is greater than or equal to int2.
int1 lt int2 int1 is less than int2.
int1 le int2 int1 is less than or equal to int2.
Logical Operators (Old-Style test):
! Not operator.
a And operator.
o Or operator.
File Testing (Old-Style test):
b filename Block special file.
c filename Character special file.
d filename Directory existence.
f filename File existence and not a directory.
g filename Set-group-ID is set.
h filename Symbolic link.
k filename Sticky bit is set.
p filename File is a named pipe.
r filename File is readable.
s filename File is nonzero size.
u filename Set-user-ID bit is set.
w filename File is writeable.
x filename File is executable.

The New test Command. With the [[ ]] compound test command, additional operators are available. Wildcards can be used in string-matching tests, and many of the errors from the old test have been eliminated. New string test operators are listed in Table 10.26.

Table 10.26. String Testing (New-Style Test)
String Testing Operator Tests For
string = pattern string matches pattern.[a]
string != pattern string does not match pattern.
string1 < string2 ASCII value of string1 is less than string2.
string1 > string2 ASCII value of string1 is greater than string2.
z string string is zero in length, null parameter.
n string string is nonzero in length, nonnull parameter.

[a] On versions newer than 1988, the == operator is permitted.

Example 10.83
(The Script)     read answer 1   if [[ $answer = [Yy]* ]]     # Test for Yes or yes or Y or y, etc.     then...     Example:     (The Script)     guess=Noone 2   if [[ $guess != [Nn]o@(one|body) ]]        # Test for Noone, noone     then. . .                                  # or Nobody, nobody...     Example:     (The Command Line) 3   [[ apples < oranges ]]     print $?     0 4   [[ apples > oranges ]]     print $?     1 5   $ name="Joe Shmoe"     $ [ $name = "Abe Lincoln" ]            # old style     ksh: Shmoe: unknown test operator 6   $ [[ $name = "Abe Lincoln" ]]          # new style     $ echo $?     1 

EXPLANATION

  1. The answer read in from the user is tested to see if it matches anything starting with Y or y.

  2. The variable guess is tested. If it is not equal to a string starting with N or n, followed by an o, and exactly one or body, for example, noone or nobody, the then command would be executed.

  3. The string apples is tested to see if it comes before oranges in the ASCII collating sequence. It does.

  4. The string apples is tested to see if it comes after oranges in the ASCII collating sequences. It does not.

  5. In the old-style test, the variable name is split into separate words. Since the = operator expects a single string as its left operand, the test command fails. To fix the problem, the variable should be enclosed in double quotes.

  6. In the new-style test, the variable is not split up into separate words; therefore, double quotes are not required around $name.

File Testing with Binary Operators. The binary operators for testing files require two operands (i.e., a file on either side of the operator). See Table 10.27 for a list of binary file testing operators.

Table 10.27. Binary File Testing and Logical Operators
Operators Tests For
Binary File Testing
file1 nt file2 True if file1 is newer than file2.
file1 ot file2 True if file1 is older than file2.
file1 ef file2 True if file1 is another name for file2.

Logical Operators. The Korn shell, like C, provides logical testing of the truth or falsity of expressions. They are listed in Table 10.28.

Table 10.28. Logical Operators
Operators Tests For
&& The and operator evaluates the expression on the left-hand side of &&; if true, the expression on the right side of && is tested and must also be true. If one expression is false, the expression is false. The && operator replaces a; e.g., (( ( $x && $y ) > 5 )).
|| The or operator evaluates the expression on the left-hand side of the || operator; if true, the expression is true; if false, the expression on the right-hand side of the || is evaluated; if true, the expression is true. Only if both expressions are false will the expression evaluate to false. The || operator replaces o; e.g., (( ( $x || $y ).

File Testing. The Korn shell provides a number of built-in test commands for checking the attributes of files, such as existence, type, permissions, etc. The file testing options (also called flags) are listed in Table 10.29.

Table 10.29. File Testing (New test Flags)
test Flag Tests For
Korn Shell Only:
a file file exists.
e file file exists (versions newer than 1988).
L file file exists and is a symbolic link.
O file You are the owner of file.
G file Your group ID is the same as file's.
S file file exists and is a socket.
Bourne and Korn Shells:
r file file exists and is readable.
w fle file exists and is writeable.
x file file exists and is executable.
f file file exists and is not a directory.
d file file exists and is a directory.
b file file exists and is a block special file.
c file file exists and is a character special file.
p file file exists and is a named pipe.
u file file exists and is setuid.
g file file exists and is setgid.
k file file exists and sticky bit is set.
s file file has a nonzero size.
Example 10.84
    (The Script) 1   file=/etc/passwd 2   if [[ -f $file && ( r $file ||  w $file) ]]     then 3     print $file is a plain file and is either readable or writeable     fi 

EXPLANATION

  1. The variable file is assigned /etc/passwd.

  2. The file test operators test if the file is a plain file and is either readable or writeable. The parentheses are used for grouping. In the old test, the parentheses had to be escaped with a backslash.

  3. If both of the tests are true, the file is a plain file, and it is either readable or writeable, this line is executed.

The if Command. The simplest form of conditional is the if command. The command following the if keyword is executed and its exit status is returned. If the exit status is zero, the command succeeded and the statement(s) after the then keyword are executed.

In the C shell and C language, the expression following the if command is a Boolean-type expression. But in the Bourne and Korn shells, the statement following the if is a command or group of commands. The exit status of the last command of the if line is used to determine whether or not to continue and execute commands under the then statement. If the exit status of the last command on the if line is zero, the commands under the then statement are executed. The fi terminates the command list to be executed after the then. If the exit status is nonzero, meaning that the command failed in some way, the statement(s) after the then statement are ignored and control goes to the line directly after the fi statement.

Conditional commands can be nested. Every if must have a corresponding fi. The fi is paired with the closest if. Using indentation to format your if blocks helps when debugging your programs.

FORMAT

if command then     # Testing command exit status     command     command fi ------------------------------------ if test expression then     # Using the test command to test expressions     command fi             or if [ expression ] then                  # Using the old-style test command--     command           # brackets replace the word test fi ---------------------------------- if [[ expression ]] then                  # New-style brackets for testing expressions     command fi ---------------------------------------------- if command then ...     if command     then     ...         if command         # Nested conditionals         then         ...         fi     fi fi 
Example 10.85
1   if ypmatch $name passwd > /dev/null 2>&1 2   then           echo Found $name! 3   fi 

EXPLANATION

  1. The ypmatch command is an NIS command that searches for its argument, name, in the NIS passwd database on the server machine. Standard output and standard error are redirected to /dev/null, the UNIX bit bucket.

  2. If the exit status of the ypmatch command is zero, the program goes to the then statement and executes commands until fi is reached.

  3. The fi terminates the list of commands following the then statement.

Using the Old-Style Bourne test. If you have been programming in the Bourne shell, the Korn shell is backward-compatible, allowing your Bourne shell scripts to be executed properly by the Korn shell. Many Bourne shell programmers, when converting to Korn shell, still use the old-style test command when evaluating expressions. If you are reading or maintaining scripts, you may find the old syntax alive and well. Therefore, a brief discussion of the old syntax may help you, even if you are writing your own scripts with the new Korn shell test command.

Example 10.86
    #!/bin/ksh     # Scriptname: are_you_ok 1   print "Are you ok (y/n) ?"     read answer 2   if [ "$answer" = Y -o "$answer" = y ]     # Old-style test     then        print "Glad to hear it." 3   fi 

EXPLANATION

  1. The user is asked the question, Are you ok (y/n) ?. The read command causes the program to wait for user input.

  2. The test command, represented by a [, is used to test expressions and returns an exit status of zero if the expression is true and nonzero if the expression is false. If the variable answer evaluates to Y or y, the commands after the then statement are executed. (The test command does not allow the use of wildcards when testing expressions.)

  3. The fi terminates the list of commands following the then statement.

Using the New-Style Korn test. The new Korn shell-style testing allows expressions to contain shell metacharacters and Korn shell operators such as && and ||.

Example 10.87
    #!/bin/ksh     # Scriptname: are_you_ok2 1   print "Are you ok (y/n) ?"     read answer 2   if [[ "$answer" = [Yy]* ]]          # New-style test     then          print "Glad to hear it." 3   fi 

EXPLANATION

  1. The user is asked the question, Are you ok (y/n) ?. The read command causes the program to wait for user input.

  2. The [[ ]] is a special Korn shell construct used to test expressions. If the answer evaluates to Y or y followed by any number of characters, the commands after the then statement are executed.

  3. The fi statement terminates the if.

Using the Old-Style Bourne test with Numbers. To test numeric expressions, the old-style Bourne shell test command and its operators are still acceptable in the Korn shell, but the new-style let command is preferred.

Example 10.88
1   if [ $# -lt 1 ]     then         print "$0: Insufficient arguments " 1>&2         exit 1 2   fi 

EXPLANATION

  1. The statement reads: If the number of arguments is less than 1, print the error message and send it to standard error. Then exit the script. The old style of testing integers is used with the test command.

  2. The fi marks the end of the block of statements after then.

The let Command and Testing Numbers. Although it is still acceptable to use single square brackets and old-style Bourne shell numeric operators for testing numeric expressions, the preferred Korn shell method is to use the double parentheses and the new C-language style numeric operators when testing numeric expressions. Note that the double brackets are only used for testing string expressions and for file tests (see Table 10.29).

Example 10.89
1   if (( $# < 1 ))     then          print "$0: Insufficient arguments " 1>&2          exit 1 2   fi 

EXPLANATION

  1. The statement reads: If the number of arguments is less than 1, print the error message and send it to standard error. Then exit the script. This is the preferred way to perform numeric tests in the Korn shell.

  2. The fi marks the end of the block of statements after then.

The if/else Command. The if/else command allows a two-way decision-making process. If the command after the if fails, the commands after the else are executed.

FORMAT

if command then    command(s) else    command(s) fi 
Example 10.90
1   if ypmatch "$name" passwd > /dev/null 2>&1 2   then           print Found $name! 3   else 4       print "Can't find $name."         exit 1 5   fi 

EXPLANATION

  1. The ypmatch command searches for its argument, $name, in the NIS passwd database. Standard output and standard error are redirected to /dev/null, the UNIX bit bucket.

  2. If the exit status of the ypmatch command is zero, program control goes to the then statement and executes commands until else is reached.

  3. The commands under the else statement are executed if the ypmatch command fails to find name in the passwd database; that is, the exit status of ypmatch must be nonzero for the commands in the else block to be executed.

  4. The print function sends output to the screen and the program exits.

  5. This marks the end of the if construct.

The if/elif/else Command. The if/elif/else command allows a multiway decision-making process. If the command following the if fails, the command following the elif is tested. If that command succeeds, the commands under its then statement are executed. If the command after the elif fails, the next elif command is checked. If none of the commands succeed, the else commands are executed. The else block is called the default.

FORMAT

if command then    command(s) elif command then    commands(s) elif command then    command(s) else    command(s) fi 

 

FORMAT

if [[ string expression ]]      or       if (( numeric expression )) then    command(s) elif [[ string expression  ]]   or       elif (( numeric expression )) then    commands(s) elif [[ string expression ]]    or       elif(( numeric expression )) then    command(s) else    command(s) fi 
Example 10.91
(The Script)     #!/bin/ksh     # Scriptname: tellme 1   read age?"How old are you? " 2   if (( age < 0 || age > 120 ))     then        print "Welcome to our planet! "        exit 1     fi 3   if (( age >= 0 && age < 13 ))     then        print "A child is a garden of verses"     elif (( age > 12 && age < 20 ))     then        print "Rebel without a cause"     elif (( age >= 20 && age < 30 ))     then        print "You got the world by the tail!!"     elif (( age >= 30 && age < 40 ))     then        print "Thirty something..." 4   else        print "Sorry I asked" 5   fi (The Output) $ tellme How old are you? 200 Welcome to our planet! $ tellme How old are you? 13 Rebel without a cause $ tellme How old are you? 55 Sorry I asked 

EXPLANATION

  1. The user is asked for input. The input is assigned to the variable age.

  2. A numeric test is performed within the double parentheses. If age is less than zero or greater than 120, the print command is executed and the program terminates with an exit status of one. The interactive shell prompt will appear. Note that the dollar sign ($) is not required to perform variable substitution when using the (( )) operators.

  3. A numeric test is performed within the double parentheses. If age is greater than zero and less than 13, the let command returns exit status zero, true.

  4. The else construct is the default. If none of the above statements are true, the else commands will be executed.

  5. The fi terminates the initial if statement.

The exit Command. The exit command is used to terminate the script and get back to the command line. You may want the script to exit if some condition does not test true. The argument to the exit command is an integer, ranging from zero to 255. When the program exits, the exit number is stored in the shell's ? variable.

Example 10.92
(The Script)     #!/bin/ksh     # Scriptname: filecheck     # Purpose: Check to see if a file exists,what type it is,     # and its permissions. 1   file=$1      # Variable is set to first command line argument 2   if [[ ! -a $file ]]     then        print "$file does not exist"        exit 1     fi 3   if [[ -d $file ]]     then        print "$file is a directory" 4   elif [[ -f $file ]]     then 5       if [[ -r $file && -w $file && -x $file ]]         then            print "You have read, write, and execute permission on            file $file"         else 6          print "You don't have the correct permissions"            exit 2         fi     else 7       print "$file is neither a file nor a directory. "         exit 3 8   fi (The Command Line) 9   $ filecheck testing     testing does not exist 10  $ echo $?     1 

EXPLANATION

  1. The first command line argument passed to this program ($1) is assigned to the variable file.

  2. The test command follows the if. If $file (after variable substitution) is a file that does not exist (note the not operator, !), the commands under the then keyword are executed. An exit value of one means that the program failed in some way (in this case, the test failed).

  3. If the file is a directory, print that it is a directory.

  4. If the file is not a directory, else if the file is a plain file, then

  5. If the file is readable, writeable, and executable, then

  6. The fi terminates the innermost if command. The program exits with an argument of two if the file does not have read, write, and execute permission.

  7. The else commands are executed if lines 2 and 3 fail. The program exits with a value of three.

  8. This fi goes with the if on line 3 in the example.

  9. The file called testing does not exist.

  10. The $? variable holds the exit status, one.

The null Command. The null command is a colon. It is a built-in, do-nothing command that returns an exit status of zero. It is used as a placeholder after an if command when you have nothing to say, but need a command or the program will produce an error message because it requires something after the then statement. Often the null command is used as an argument to the loop command to make the loop a forever loop or for testing variable expression modifiers such as {EDITOR: /bin/vi}.

Example 10.93
(The Script) 1   name=Tom 2   if grep "$name" databasefile > /dev/null 2>&1     then 3       : 4   else         print "$1 not found in databasefile"         exit 1     fi 

EXPLANATION

  1. The string Tom is assigned to the variable name.

  2. The if command tests the exit status of the grep command. If Tom is found in databasefile, the null command is executed and does nothing.

  3. The colon is the null command. It always exits with a zero exit status.

  4. What we really want to do is print an error message and exit if Tom is not found. The commands after the else will be executed if the grep command fails.

Example 10.94
(The Script) 1   : ${EDITOR:=/bin/vi} 2   echo $EDITOR 

EXPLANATION

  1. The colon command takes an argument that is evaluated by the shell. The expression ${EDITOR:=/bin/vi} is used as an argument to the colon command. If the variable EDITOR has been previously set, its value will not be changed; if it has not been set, the value /bin/vi will be assigned to it. The Korn shell would have responded with an error such as ksh: /bin/vi: not found if the colon command had not preceded the expression.

  2. The value of the EDITOR variable is displayed.

10.2.7 The case Command

The case command is a multiway branching command used as an alternative to the if/elif commands. The value of the case variable is matched against value1, value2, and so forth until a match is found. When a value matches the case variable, the commands following the value are executed until the double semicolons are reached. Then, instruction starts after the word esac (case spelled backwards).

If a case variable is not matched, the program executes commands after the *) , the default value, until the double semicolons or esac is reached. The *) value serves the same purpose as the else statement in if/else conditionals. The case values can use shell wildcards and the vertical bar (pipe symbol ) for oring two values.

FORMAT

case variable value1)     command(s);; value2)     command(s);; *)     command(s);; esac 
Example 10.95
(The Script)     #!/bin/ksh     # Scriptname: xtermcolor     # Sets the xterm foreground color (the color of the prompt and     # input typed for interactive windows. 1   read color?"Choose a foreground color for your terminal?" 2   case "$color" in 3   *[Bb]l??) 4       xterm -fg blue -fn terminal & 5           ;; 6   *[Gg]reen)          xterm -fg darkgreen -fn terminal &             ;; 7   red | orange)                   # The vertical bar means "or"        xterm -fg "$color" -fn terminal &            ;; 8   *)  xterm -fn terminal &        # default            ;; 9   esac 10  print "Out of case..." 

EXPLANATION

  1. The user is asked for input. The input is assigned to the variable color.

  2. The case command evaluates the expression $color.

  3. If color begins with a B or b, followed by the letter l and any two characters, the case expression matches the first value. The value is terminated with a single closed parenthesis. The wildcards are shell metacharacters.

  4. The statement is executed if the value in line 3 matches the case expression.The xterm command sets the foreground color to blue.

  5. The double semicolons are required after the last command in this block of commands. Control branches to line 10, after the semicolons are reached.

  6. If the case expression matches a G or g, followed by the letters r-e-e-n, the xterm window foreground color is set to dark green.The double semicolons terminate the block of statements and control branches to line 10.

  7. The vertical bar is used as an or conditional operator. If the case expression matches either red or orange, the xterm command is executed.

  8. This is the default value. If none of the above values match the case expression, the command(s) after the *) value are executed. The default color for the terminal foreground is black.

  9. The esac statement (case spelled backwards) terminates the case command.

  10. After one of the values is matched, execution continues here.

The case Command and the here document. Often the here document is used to create a menu. After the user has selected a choice from the menu, the case command is used to match against one of the choices. The Korn shell also provides a select loop for creating menus.

Example 10.96
(The .profile File)     print "Select a terminal type " 1   cat << EOF         1) vt120         2) wyse50         3) ansi         4) sun 2   EOF 3   read TERM 4   case "$TERM" in     1)  export TERM=vt120         ;;     2)  export TERM=wyse50         ;;     3)  export TERM=ansi         ;;     *)  export TERM=sun         ;; 5   esac     print "TERM is $TERM" 

EXPLANATION

  1. A here document is used to display a menu of choices.

  2. EOF is the user-defined terminator. Input for the here document stops here.

  3. The read command waits for user input and assigns it to the TERM variable.

  4. The case command evaluates the variable TERM and matches it against one of the numbers in the list. If a match is found, the terminal is set.

  5. The case command terminates with esac.

10.2.8 Looping Commands

The looping commands are used to execute a command or group of commands a set number of times, or until a certain condition is met. The Korn shell has four types of loops: the for loop, while loop, until loop, and select loop.

The for Command. The for looping command is used to execute commands for each member of a set of arguments. You might use this loop to execute the same commands on a list of files or usernames. The for command is followed by a user-defined variable, the keyword in, and a list of words. The first time in the loop, the first word from the wordlist is assigned to the variable, and then shifted off. The next time around the loop, the second word is assigned to the variable, and so on. The body of the loop starts at the do keyword and ends at the done keyword. When all of the words in the list have been shifted off, the loop ends and program control continues after the done keyword.

FORMAT

for variable in wordlist do     command(s) done 
Example 10.97
(The Script) 1   for pal in Tom Dick Harry Joe 2   do 3       print "Hi $pal" 4   done 5   print "Out of loop" (The Output)     Hi Tom     Hi Dick     Hi Harry     Hi Joe     Out of loop 

EXPLANATION

  1. This for loop will iterate through the list of names, Tom, Dick, Harry, and Joe, shifting each one off (to the left) after it is assigned to the variable pal. As soon as all of the words are shifted and the wordlist is empty, the loop ends and execution starts after the done keyword. The word following the for command, pal, is a variable that will be assigned the value after the in keyword, one at a time, for each iteration of the loop. The first time in the loop, the variable pal will be assigned the word Tom. The second time through the loop, pal will be assigned Dick, the next time pal will be assigned Harry, and the last time, pal will be assigned Joe.

  2. The do keyword is required after the wordlist. If it is used on the same line, the list must be terminated with a semicolon. For example:

    for pal in Tom Dick Harry Joe; do

  3. This is the body of the loop. After Tom is assigned to the variable pal, the commands in the body of the loop, that is, all commands between the do and the done keywords, are executed.

  4. The done keyword ends the loop. If there are no words left to be processed in the wordlist on line 1, the loop exits, and execution starts at line 5.

  5. This line is executed when the loop terminates.

Example 10.98
(The Command Line) 1   $ cat mylist     tom     patty     ann     jake ------------------------------------------------------------- (The Script) 2   for person in $(< mylist)   #same as for person in 'cat mylist'     do 3         mail $person < letter           print $person was sent a letter. 4   done 5   print "The letter has been sent." 

EXPLANATION

  1. The contents of a file, called mylist, are displayed.

  2. Command substitution is performed and the contents of mylist become the wordlist. The first time in the loop, tom is assigned to the variable person, and then shifted off, to be replaced with patty, and so on.

  3. In the body of the loop, each user is mailed a copy of a file called letter.

  4. The done keyword marks the end of this loop iteration.

  5. When all of the users in the list have been sent mail, the loop will exit, and this line will be executed.

Example 10.99
1   for file in *.c 2   do         if [[ -f $file ]] ; then            cc $file -o ${file%.c}         fi     done 

EXPLANATION

  1. The wordlist will consist of all files in the current working directory ending with the extension .c (C source files). Each filename will be assigned to variable file, in turn, for each iteration of the loop.

  2. When the body of the loop is entered, the file will be tested to make sure it exists and is a real file. If so, it will be compiled. ${file%.c} expands to the filename without its extension.

The $* and $@ Variables in Wordlists. When expanded, the $* and $@ are the same unless enclosed in double quotes. "$*" evaluates to one string, whereas "$@" evaluates to a list of separate words.

Example 10.100
(The Script)     #!/bin/ksh 1   for name in $*     # or for name in $@ 2   do         echo Hi $name 3   done ----------------------------------------------------------------- (The Command Line) $ greet Dee Bert Lizzy Tommy Hi Dee Hi Bert Hi Lizzy Hi Tommy 

EXPLANATION

  1. $* and $@ expand to a list of all the positional parameters, in this case, the arguments passed in from the command line: Dee, Bert, Lizzy, and Tommy. Each name in the list will be assigned, in turn, to the name variable in the for loop.

  2. The commands in the body of the loop are executed until the list is empty.

  3. The done keyword marks the end of the loop body.

The while Command. The while evaluates the command immediately following it, and if its exit status is zero, the commands in the body of the loop (commands between do and done) are executed. When the done keyword is reached, control is returned to the top of the loop and the while command checks the exit status of the command again. Until the exit status of the command being evaluated by the while becomes nonzero, the loop continues. When the exit status reaches nonzero, program execution starts after the done keyword. If the exit status never becomes nonzero, the loop goes around and around infinitely. (Of course, pressing Control-C or Control-\ will stop the looping.)

FORMAT

while command do    command(s) done 
Example 10.101
(The Script) 1   num=0                   # Initialize num 2   while (( num < 10 ))    # Test num with the let     do        print -n $num 3      (( num=num + 1 ))      # Increment num     done     print "\nAfter loop exits, continue running here" (The Output) 0123456789 After loop exits, continue running here 

EXPLANATION

  1. This is the initialization step. The variable num is assigned zero.

  2. The while command is followed by the let command. If the value of num is less than 10, the body of the loop is entered.

  3. In the body of the loop, the value of num is incremented by one. If the value of num was never changed, the loop would iterate infinitely or until the process was killed.

Example 10.102
(The Script)     #!/bin/ksh     # Scriptname: quiz 1   read answer?"Who was the U.S. President in 1992? " 2   while [[ $answer != "Bush" ]] 3   do         print "Wrong try again!" 4       read answer 5   done 6   print Good guess! (The Output) $ quiz Who was the U.S. President in 1992? George Wrong try again! Who was the U.S. President in 1992? I give up Wrong try again! Who was the U.S. President in 1992? Bush Good guess! 

EXPLANATION

  1. The read command prints the string after the question mark (?), Who was the U.S. President in 1992?, and waits for input from the user. The input will be stored in the variable answer.

  2. The while loop is entered and the test command, [[, evaluates the expression. If the variable answer does not equal the string Bush, the body of the loop is entered and commands between the do and done are executed.

  3. The do keyword is the start of the loop body.

  4. The user is asked to reenter input.

  5. The done keyword marks the end of the loop body. Control is returned to the top of the while loop, and the expression is tested again. As long as $answer does not evaluate to Bush, the loop will continue to iterate. When the user's input is Bush, the loop ends. Program control goes to line 6.

Example 10.103
(The Script) 1   go=1     print Type q to quit. 2   while let go or (( go ))     do        print I love you.        read word 3      if [[ $word = [qQ]* ]]        then            print "I'll always love you" 4          go=0         fi 5    done (The Output) $ sayit Type q to quit. I love you. I love you. I love you. I love you. I love you. q I'll always love you $ 

EXPLANATION

  1. The variable go is assigned 1.

  2. The loop is entered. The let command tests the expression. The expression evaluates to one. The program goes into the body of the while loop and executes commands from the do keyword to the done keyword.

  3. If the user enters a q or Q as input to the variable word, the commands between then and fi are executed. Anything else will cause I love you. to be displayed.

  4. The variable go is assigned zero. When program control starts at the top of the while loop, the expression will be tested. Since the expression evaluates to false, the loop exits and the script starts execution after the done keyword on line 5.

  5. The done marks the end of the body of the loop.

The until Command. The until command is used like the while command, but evaluates the exit status in the opposite way. The until evaluates the command immediately following it, and if its exit status is not zero, the commands in the body of the loop (commands between do and done) are executed. When the done keyword is reached, control is returned to the top of the loop and the until command checks the exit status of the command again. Until the exit status of the command being evaluated by until becomes zero, the loop continues. When the exit status reaches zero, program execution starts after the done keyword.

FORMAT

until command do    command(s) done 
Example 10.104
    #!/bin/ksh 1   until who | grep linda 2   do        sleep 5 3   done     talk linda@dragonwings 

EXPLANATION

  1. The until loop tests the exit status of the last command in the pipeline, grep. The who command lists who is logged on this machine and pipes its output to grep. The grep command will return zero exit status (success) only when it finds user linda.

  2. If user linda has not logged in, the body of the loop is entered and the program sleeps for 5 seconds.

  3. When linda logs on, the exit status of the grep command will be zero and control will go to the statements following the done keyword.

Example 10.105
    #!/bin/ksh 1   hour=0 2   until (( hour > 23 ))     do 3      case "$hour" in        [0-9]|1[0-1]) print "Good morning!"               ;;        12) print "Lunch time"               ;;        1[3-7]) print "Siesta time"               ;;        *) print "Good night"               ;;        esac 4      (( hour+=1 )) 5   done 

EXPLANATION

  1. The hour variable is assigned zero. The variable must be initialized before being used in the until loop.

  2. The until command is followed by the let command. If the hour is not greater than 23, that is, the exit status is nonzero, the loop body is entered.

  3. The case command matches the value of the hour variable against one of the hour values, or matches the default, executing the command that applies.

  4. The hour is incremented by one; otherwise, the hour will never become greater than 23 and the loop will never exit. Control is returned to the until command and the hour is evaluated again.

  5. The done keyword marks the end of the loop. When the hour is greater than 23, control will go to the line under the done, if there is one; otherwise, the program is exited.

The select Command and Menus. The here document is an easy method for creating menus, but the Korn shell introduces a new loop, called the select loop, which is used primarily for creating menus. A menu of numerically listed items is displayed to standard error. The PS3 prompt is used to prompt the user for input; by default, PS3 is #?. After the PS3 prompt is displayed, the shell waits for user input. The input should be one of the numbers in the menu list. The input is stored in the special Korn shell REPLY variable. The number in the REPLY variable is associated with the string to the right of the parentheses in the list of selections.[7]

The case command is used with the select command to allow the user to make a selection from the menu and, based on that selection, execute commands. The LINES and COLUMNS variables can be used to determine the layout of the menu items displayed on the terminal. The output is displayed to standard error, each item preceded by a number and closing parenthesis, and the PS3 prompt is displayed at the bottom of the menu. Since the select command is a looping command, it is important to remember to use either the break command to get out of the loop, or the exit command to exit the script.

FORMAT

select var in wordlist do    command(s) done 
Example 10.106
(The Script)     #!/bin/ksh     # Program name: goodboys 1   PS3="Please choose one of the three boys : " 2   select choice in tom dan guy 3   do 4       case $choice in         tom)             print Tom is a cool dude! 5           break;;         # break out of the select loop 6        dan | guy )             print Dan and Guy are both sweethearts.             break;;          *) 7           print " $REPLY is not one of your choices" 1>&2             print "Try again."             ;; 8       esac 9   done (The Command Line) $ goodboys 1) tom 2) dan 3) guy Please choose one of the three boys : 2 Dan and Guy are both sweethearts. $ goodboys 1) tom 2) dan 3) guy Please choose one of the three boys : 4 4 is not one of your choices Try again. Please choose one of the three boys : 1 Tom is a cool dude! $ 

EXPLANATION

  1. The PS3 variable is assigned the prompt that will appear below the list of menu selections. After the prompt is displayed, the program waits for user input. The input is stored in the built-in variable called REPLY.

  2. The select command is followed by the variable choice. This syntax is similar to that of the for loop. The variable choice is assigned, in turn, each of the items in the list that follows it, in this case, tom, dan, and guy. It is this wordlist that will be displayed in the menu, preceded by a number and a right parenthesis.

  3. The do keyword indicates the start of the body of the loop.

  4. The first command in the body of the select loop is the case command. The case command is normally used with the select loop. The value in the REPLY variable is associated with one of the choices: 1 is associated with tom, 2 is associated with dan, and 3 is associated with guy.

  5. If tom is the choice, after printing the string Tom is a cool dude!, the break command causes the select loop to be exited. Program control starts after the done keyword.

  6. If either menu item, 2 (dan) or 3 (tom), is selected, the REPLY variable contains the user's selection. If the selection is not 1, 2, or 3, an error message is sent to standard error. The user is asked to try again and control starts at the beginning of the select loop.

  7. The end of the case command.

  8. The end of the select loop.

Example 10.107
(The Script)     #!/bin/ksh     # Program name: ttype     # Purpose: set the terminal type     # Author: Andy Admin 1   COLUMNS=60 2   LINES=1 3   PS3="Please enter the terminal type: " 4   select choice in wyse50 vt200 vt100 sun     do 5      case $REPLY in        1) 6          export TERM=$choice            print "TERM=$choice"            break;;               # break out of the select loop        2 | 3 )            export TERM=$choice            print "TERM=$choice"            break;;        4)            export TERM=$choice            print "TERM=$choice"            break;;        *) 7          print "$REPLY is not a valid choice. Try again" 1>&2                 ;;         esac 8   done (The Command Line) $ ttype 1) wyse50    2) vt200   3) vt100    4) sun Please enter the terminal type : 4 TERM=sun $ ttype 1) wyse50    2) vt200   3) vt100    4) sun Please enter the terminal type : 3 TERM=vt100 $ ttype 1) wyse50    2) vt200   3) vt100    4) sun Please enter the terminal type : 7 7 is not a valid choice. Try again. Please enter the terminal type: 2 TERM=vt200 

EXPLANATION

  1. The COLUMNS variable is set to the width of the terminal display in columns for menus created with the select loop. The default is 80.

  2. The LINES variable controls the vertical display of the select menu on the terminal. The default is 24 lines. By changing the LINES value to 1, the menu items will be printed on one line, instead of vertically as in the last example.

  3. The PS3 prompt is set and will appear under the menu choices.

  4. The select loop will print a menu with four selections: wyse50, vt200, vt100, and sun. The variable choice will be assigned one of these values based on the user's response held in the REPLY variable. If REPLY is 1, wyse50 is assigned to choice; if REPLY is 2, vt200 is assigned to choice; if REPLY is 3, vt100 is assigned to choice; and if REPLY is 4, sun is assigned to choice.

  5. The REPLY variable evaluates to the user's input selection.

  6. The terminal type is assigned, exported, and printed.

  7. If the user does not enter a number between 1 and 4, he or she will be prompted again. Note that the menu does not appear, just the PS3 prompt. To make the menu reappear, set the REPLY variable to null. Type above line 8: REPLY =

  8. The end of the select loop.

Looping Commands. If some condition occurs, you may want to break out of a loop, return to the top of the loop, or provide a way to stop an infinite loop. The Korn shell provides loop control commands to control loops.

The shift Command. The shift command shifts the parameter list to the left a specified number of times. The shift command without an argument shifts the parameter list once to the left. Once the list is shifted, the parameter is removed permanently. Often the shift command is used in while loops when iterating through a list of positional parameters.

FORMAT

shift [n] 
Example 10.108
(Without a Loop) (The Script)     #!/bin/ksh     # Scriptname: doit0 1   set joe mary tom sam 2   shift 3   print $* 4   set $(date) 5   print $* 6   shift 5 7   print $* 8   shift 2 (The Output)     $ doit0 3   mary tom sam 5   Sun Sep 9 10:00:12 PDT 2001 7   2001 8   ksh: shift: bad number 

EXPLANATION

  1. The set command sets the positional parameters. $1 is assigned joe, $2 is assigned mary, $3 is assigned tom, and $4 is assigned sam.

  2. The shift command shifts the positional parameters to the left; joe is shifted off.

  3. The parameter list is printed after the shift. $* represents all of the parameters.

  4. The set command resets the positional parameters to the output of the UNIX date command.

  5. The new parameter list is printed.

  6. This time the list is shifted five times to the left.

  7. The new parameter list is printed.

  8. By attempting to shift more times than there are parameters, the shell sends a message to standard error.

Example 10.109
(With a Loop) (The Script)     #!/bin/ksh     # Usage: doit [args] 1   while (( $# > 0 ))     do 2      print $* 3      shift 4   done (The Command Line) $ doit a b c d e a b c d e b c d e c d e d e e 

EXPLANATION

  1. The while command tests the numeric expression. If the number of positional parameters ($#) is greater than zero, the body of the loop is entered. The positional parameters are coming from the command line as arguments. There are five.

  2. All the positional parameters are printed.

  3. The parameter list is shifted once to the left.

  4. The body of the loop ends here; control returns to the top of the loop. The parameter list has decreased by one. After the first shift, $# is four. When $# has been decreased to zero, the loop ends.

The break Command. The built-in break command is used to force immediate exit from a loop, but not from a program. (To leave a program, the exit command is used.) After the break command is executed, control starts after the done keyword. The break command causes an exit from the innermost loop, so if you have nested loops, the break command takes a number as an argument, allowing you to exit out of any number of outer loops. The break is useful for exiting from an infinite loop.

Format

break [n] 
Example 10.110

graphics/10prfig02.gif

EXPLANATION

  1. The true command is a UNIX command, and an alias for the colon command in the Korn shell. It always exits with zero status and is often used to start an infinite loop. (The null command (:) can be used to do the same thing.) The body of the loop is entered.

  2. The user is asked for input. The user's input is assigned to the variable answer.

  3. If answer evaluates to Y, y, Yes, Yup, or Ya (anything beginning with Y or y), the break command is executed and control goes to line 6. The line Here we are is printed. Until the user answers something that starts with a Y or y, the program will continue to ask for input. This could go on forever!

  4. If the test fails in line 3, the else commands are executed. When the body of the loop ends at the done keyword, control starts again at the top of the while at line 1.

  5. The end of the loop body.

  6. Control starts here after the break command is executed.

The continue Command. The continue command starts back at the top of the loop if some condition becomes true. All commands below the continue will be ignored. The continue command returns control to the top of the innermost loop; if nested within a number of loops, the continue command may take a number as its argument. Control can be started at the top of any number of outer loops.

FORMAT

continue [n] 
Example 10.111

graphics/10prfig03.gif

EXPLANATION

  1. The for loop will iterate through a list of names stored in a file called mail_list. Each time a name from the list is assigned to the variable name, it is shifted off the list and replaced by the next name on the list.

  2. The name matches richard; the continue command is executed. Since richard has already been shifted off, the next user, melanie, is assigned to the variable name.

  3. The continue command returns control to the top of the loop, skipping any commands in the rest of the loop body.

  4. All users in the list, except richard, will be mailed a copy of the file memo.

  5. The end of loop body.

Nested Loops. If using nested loops, the break and continue commands let you control which loop to terminate.

Example 10.112

graphics/10prfig04.gif

EXPLANATION

  1. The true command always returns an exit status of zero. The loop is designed to go forever unless you use loop control commands.

  2. The for loop is entered.

  3. The for loop will loop through each of the names in the list. If the user variable begins with a D or d, the continue command causes control to go to the top of the while loop. Without an argument, the continue command would start control at the top of the for loop. The argument 2 tells the shell to go to the top of the second enclosing loop and restart execution there.

  4. The while loop is nested. The true command always exits with zero status. The loop will go forever.

  5. The break command terminates the outermost while loop. Execution starts at line 9.

  6. The done keyword marks the end of the innermost while loop.

  7. This done keyword marks the end of the for loop.

  8. This done keyword marks the end of outermost while loop.

  9. Out of the loop.

I/O Redirection and Loops. The Korn shell allows you to use redirection and pipes in loops. Unlike the Bourne shell, the loop runs in this shell, not a subshell. Variables set within the loop will still be set when the loop exits.

Redirect the Output of a Loop to a File. Instead of sending the output of a loop to the screen, it can be redirected to a file or a pipe. See Example 10.113.

Example 10.113
    (The Command Line) 1   $ cat memo     abc     def     ghi ------------------------------------------------------------------ (The Script)     #!/bin/ksh     # Program name: numberit     # Put line numbers on all lines of memo 2   if (( $# < 1 ))     then         print "Usage: $0 filename " >&2         exit 1     fi 3   integer count=1              # Initialize count 4   cat $1 | while read line     # Input is coming from memo     do 5       (( count == 1 )) && print "Processing file $1..." > /dev/tty 6       print $count $line 7       (( count+=1 )) 8   done > tmp$$                 # Output is going to a temporary file 9   mv tmp$$ $1 (The Command Line) 10  $ numberit memo     Processing file memo... 11  $ cat memo     1 abc     2 def     3 ghi 

EXPLANATION

  1. The contents of file memo are displayed.

  2. If the number of arguments is less than one, a usage message is sent to standard error, the screen.

  3. The count variable is declared an integer and is assigned the value 1.

  4. The UNIX cat command displays the contents of the filename stored in $1, and the output is piped to the while loop. The read command is assigned the first line of the file the first time in the loop, the second line of the file the next time through the loop, and so forth.

  5. The output of this print statement is sent to /dev/tty, the screen. If not explicitly redirected to /dev/tty, the output will be redirected to tmp$$ on line 8.

  6. The print function prints the value of count, followed by the line in the file.

  7. The count variable is incremented by 1.

  8. The output of this entire loop, with the exception of line 3, is redirected to the file tmp$$ (where $$ evaluates to the PID of this process). The tmp file is given a unique name by appending the PID of this process to its name.

  9. The tmp file is renamed to the name of the file that was assigned to $1.

  10. The program is executed. The file to be processed is called memo.

  11. The file is displayed with line numbers.

Pipe the Output of a Loop to a UNIX Command. The output of a loop can be redirected from the screen to a pipe. See Example 10.114.

Example 10.114
(The Script) 1   for i in 7 9 2 3 4 5 2   do        print $i 3   done | sort  n (The Output) 2 3 4 5 7 9 

EXPLANATION

  1. The for loop iterates through a list of unsorted numbers.

  2. In the body of the loop, the numbers are printed. This output will be piped into the UNIX sort command.

  3. The pipe is created after the done keyword.

Running Loops in the Background. If the loop is going to take a while to process, it can be run as a background job so that the rest of the program can continue.

Example 10.115
1   for person in bob jim joe sam     do 2      mail $person < memo 3   done & 

EXPLANATION

  1. The for loop shifts through each of the names in the wordlist: bob, jim, joe, and sam. Each of the names is assigned to the variable person, in turn.

  2. In the body of the loop, each person is sent the contents of the file memo.

  3. The ampersand at the end of the done keyword causes the loop to be executed in the background. The program will continue to run while the loop is executing.

The exec Command and Loops. The exec command can be used to close standard input or output without creating a subshell.

Example 10.116
(The File) 1   cat tmp     apples     pears     bananas     peaches     plums ----------------------------------------------------------- (The Script)     #!/bin/ksh     # Scriptname: speller     # Purpose: Check and fix spelling errors in a file     # 2   exec < tmp         # Opens the tmp file 3   while read line    # Read from the tmp file     do 4       print $line 5       print  n "Is this word correct? [Y/N] " 6       read answer < /dev/tty    # Read from the terminal         case $answer in         [Yy]*)             continue                ;;         *)             print "New word? " 7           read word < /dev/tty             sed "s/$line/$word/" tmp > error             mv error tmp 8           print $word has been changed.                 ;;         esac     done 

EXPLANATION

  1. The contents of the tmp file are displayed.

  2. The exec command changes standard input (file descriptor 0), so that instead of input coming from the keyboard, it is coming from the tmp file.

  3. The while loop starts. The read command gets a line of input from the tmp file.

  4. The value stored in the line variable is printed to the screen.

  5. The user is asked if the word is correct.

  6. The read command gets the user's response from the terminal, /dev/tty. If the input is not redirected directly from the terminal, it will continue to be read from the file tmp, still opened for input.

  7. The user is again asked for input, and the input is redirected from the terminal, /dev/tty.

  8. The new word is displayed.

The IFS and Loops. The IFS, the shell's internal field separator, evaluates to spaces, tabs, and the newline character. It is used as a word (token) separator for commands that parse lists of words such as read, set, for, and select. It can be reset by the user if a different separator will be used in a list. It is a good idea to save the original value of the IFS in another variable before changing it. Then it is easy to return to its default value.

Example 10.117
(The Script)     #!/bin/ksh     # Script is called runit.     # IFS is the internal field separator and defaults to     # spaces, tabs, and newlines.     # In this script it is changed to a colon. 1   names=Tom:Dick:Harry:John 2   OLDIFS="$IFS"             # Save the original value of IFS 3   IFS=":" 4   for persons in $names     do 5       print Hi $persons     done 6   IFS="$OLDIFS"             # Reset the IFS to old value 7   set Jill Jane Jolene      # Set positional parameters 8   for girl in $*     do         print Howdy $girl     done (The Output) $ runit Hi Tom Hi Dick Hi Harry Hi John Howdy Jill Howdy Jane Howdy Jolene 

EXPLANATION

  1. The names variable is set to the string Tom:Dick:Harry:John. Each of the words is separated by a colon.

  2. The value of IFS is assigned to another variable, OLDIFS. Since the value of the IFS is whitespace, it must be quoted to preserve the whitespace.

  3. The IFS is assigned a colon. Now the colon is used to separate words.

  4. After variable substitution, the for loop will iterate through each of the names using the colon as the internal field separator between the words.

  5. Each of the names in the wordlist is displayed.

  6. IFS is reassigned its original values, stored in OLDIFS.

  7. The positional parameters are set. $1 is assigned Jill, $2 is assigned Jane, and $3 is assigned Jolene.

  8. $* evaluates to all the positional parameters, Jill, Jane, and Jolene. The for loop assigns each of the names to the girl variable, in turn, through each iteration of the loop.

10.2.9 Arrays

Korn shell arrays are one-dimensional arrays that may contain up to 1,024 (size varies) elements consisting of words or integers. The index starts at zero. Each element of an array can be set or unset individually. Values do not have to be set in any particular order. For example, you can assign a value to the tenth element before you assign a value to the first element. An array can be set using the set command with the A option.

Associative arrays are supported under versions of the Korn shell that are more recent than 1988.

Example 10.118
(The Command Line) 1   $ array[0]=tom     $ array[1]=dan     $ array[2]=bill 2   $ print ${array[0]}     # Curly braces are required.     tom 3   $ print ${array[1]}     dan 4   $ print ${array[2]}     bill 5   $ print ${array[*]}     # Display all elements.     tom dan bill 6   $ print ${#array[*]}    # Display the number of elements.     3 

EXPLANATION

  1. The first three elements of the array are assigned values. The index starts at zero.

  2. The value of the first array element, tom, is printed. Make sure you remember to surround the variable with curly braces. $array[0] would print tom[0].

  3. The value of the second element of the array, dan, is printed.

  4. The value of the third element of the array, bill, is printed.

  5. All elements in the array are printed.

  6. The number of elements in the array are printed. An array can be declared with typeset if you know the size and type.

Example 10.119
(At The Command Line) 1  $ typeset  i ints[4]     # Declare an array of four integers. 2  $ ints[0]=50    $ ints[1]=75    $ ints[2]=100 3  $ ints[3]=happy    ksh: happy: bad number 

EXPLANATION

  1. The typeset command creates an array of 4 integers.

  2. Integer values are assigned to the array.

  3. A string value is assigned to the fourth element of the array, and the Korn shell sends a message to standard error.

Creating Arrays with the set Command. You can assign the values of an array using the set command. The first word after the A option is the name of the array; the rest of the words are the elements of the array.

Example 10.120
(The Command Line) 1   $ set  A fruit apples pears peaches 2   $ print ${fruit[0]}     apples 3   $ print ${fruit[*]}     apples pears peaches 4   $ fruit[1]=plums 5   $ print ${fruit[*]}     apples plums peaches 

EXPLANATION

  1. The set command with the A option creates an array. The name of the array, fruit, follows the A option. Each of the elements of the fruit array follow its name.

  2. Subscripts start at zero. Curly braces are required around the variable for it to be evaluated properly. The first element of the array is printed.

  3. When the asterisk is used as a subscript, all elements of the array are displayed.

  4. The second element of the array is reassigned the value plums.

  5. All elements of the array are displayed.

10.2.10 Functions

Korn shell functions are similar to those used in the Bourne shell, and are used to modularize your program. A function is a collection of one or more commands that can be executed simply by entering the function's name, similar to a built-in command. Here is a review of some of the important rules about using functions.

  1. The Korn shell executes built-in commands first, then functions, and then executables. Functions are read into memory once when they are defined, not every time they are referenced.

  2. A function must be defined before it is used; therefore, it is best to place function definitions at the beginning of the script.

  3. The function runs in the current environment; it shares variables with the script that invoked it, and lets you pass arguments by setting them as positional parameters. The present working directory is that of the calling script. If you change the directory in the function, it will be changed in the calling script.

  4. In the Korn shell, you can declare local variables in the function using the typeset command. Ksh functions can be exported to subshells.

  5. The return statement returns the exit status of the last command executed within the function or the value of the argument given, and cannot exceed a value of 255.

  6. To list functions and definitions, use the preset alias, functions.

  7. Traps are local to functions and will be reset to their previous value when the function exits (not so with the Bourne shell).

  8. Functions can be recursive, that is, call themselves. Recursion should be handled carefully. The Korn shell will warn you otherwise with the message, recursion too deep.

  9. Functions can be autoloaded; they are defined only if referenced. If never referenced, they are not loaded into memory.

  10. Versions of the Korn shell that are more recent than 1988 also support discipline functions, passing variables by reference, and compound variables. A built-in command is no longer found before a function of the same name. In older versions it was necessary to use a combination of aliases and functions to write a function that would override a built-in command.[8]

Defining Functions. A function must be defined before it can be invoked. Korn shell functions are defined with the keyword function preceding the function name. The curly braces must have a space on the inside of each brace. (Please see Bourne shell functions for the older-style function definition, still compatible in Korn shell scripts.)

FORMAT

function function_name { commands; commands; } 
Example 10.121
function usage { print "Usage $0 [-y] [-g] " ; exit 1; } 

EXPLANATION

The function name is usage. It is used to print a diagnostic message and exit the script if the script does not receive the proper arguments, either y or g.

Listing and Unsetting Functions. To list local function definitions, type: typeset f. To list exported function definitions, type: typeset fx. To unset a function, type: unset f function_name. See the typeset command, Table 10.30.

Local Variables and the Return Value. The typeset command can be used to create local variables. These variables will be known only in the function where they are created. Once out of the function, the local variables are undefined.

The return value of a function is really just the value of the exit status of the last command in the script unless a specific return command is used. If a value is assigned to the return command, that value is stored in the ? variable. It can hold an integer value between 0 and 255. Because the return command is limited to returning only integer values, you can use command substitution to return the output of a function and assign the output to a variable, just as you would if getting the output of a UNIX command.

Example 10.122

graphics/10prfig05.gif

EXPLANATION

  1. The function called increment is defined.

  2. The typeset command defines the variable sum to be local to this function.

  3. The return built-in command, when given an argument, returns to the main script after the line where the function was invoked and stores its argument in the ? variable. In the script, the increment function is called with an argument.

  4. The increment function is called with an argument of 5.

  5. The exit status of the function is stored in ? unless an explicit argument is given to the return command. The return command argument specifies a return status for the function, its value is stored in the ? variable, and it must be an integer between 0 and 255.

  6. Since sum was defined as a local variable in the function increment, it is not defined in the script that invoked the function. Nothing is printed.

Example 10.123
(Using Command Substitution) (The Script)     # Scriptname: do_square     #!/bin/ksh 1   function square {      (( sq = $1 * $1 ))        print "Number to be squared is $1." 2      print "The result is $sq "     } 3   read number?"Give me a number to square. " 4   value_returned=$(square $number) 5   print $value_returned (The Output)     $ do_square 5   Number to be squared is 10. The result is 100 

EXPLANATION

  1. The function called square is defined. It will multiply its argument times itself.

  2. The result of squaring the number is printed.

  3. The user is asked for input.

  4. The function square is called with a number (input from the user) as its argument. Command substitution is performed because the function is enclosed in parentheses preceded by a $. The output of the function (both of its print statements) is assigned to the variable value_returned.

  5. The command substitution removes the newline between the strings Number to be squared is and The result is 100.

Exported Functions. Function definitions are not inherited by subshells unless you define them in the ENV file with the typeset command, e.g., typeset fx function_names.

You can export functions with typeset fx from the current Korn shell to a script, or from one script to another, but not from one invocation of ksh to the next (e.g., a separate invocation means that if you type ksh at the prompt, a brand new shell is started up). Exported function definitions will not be inherited by the new shell.

Example 10.124
(The First Script)     $ cat calling_script     #!/bin/ksh 1   function sayit { print "How are ya $1?" ; } 2   typeset  fx sayit   # Export sayit to other scripts 3   sayit Tommy 4   print "Going to other script" 5   other_script        # Call other_script     print "Back in calling script" **************************************************************** (The Second Script)     $ cat other_script            NOTE: This script cannot be invoked with #!/bin/ksh 6   print "In other script " 7   sayit Dan 8   print "Returning to calling script" (The Output)     $ calling_script 3   How are ya Tommy? 4   Going to other script 6   In other script 7   How are ya Dan? 8   Returning to calling script     Back in calling script 

EXPLANATION

  1. The function sayit is defined. It will accept one argument to be stored in $1.

  2. The typeset command with the fx option allows the function to be exported to any script called from this script.

  3. The function sayit is invoked with Tommy as an argument. Tommy will be stored in $1 in the function.

  4. After the sayit function terminates, the program resumes here.

  5. The script, called other_script, is executed.

  6. We are now in the other script. This script is called from the first script, sayit. It cannot start with the line #!/bin/ksh because this line causes a ksh subshell to be started, and exporting functions does not work if a separate Korn shell is invoked.

  7. The function sayit is invoked. Dan is passed as an argument, which will be stored in $1 in the function.

  8. After this line is printed, other_script terminates and control goes back to the calling script at the line where it left off after the function was invoked.

10.2.11 The typeset Command and Function Options

The typeset command is used to display function attributes.

Table 10.30. typeset and Function Options
Option What It Does
typeset f Displays all functions and their values. Must have a history file, as all function definitions are stored there.
typeset +f Displays just function names.
typeset fx Displays all function definitions that will be exported across shell scripts, but not as a separate invocation of ksh.
typeset fu func func is the name of a function that has not yet been defined.

Autoloaded Functions. An autoloaded function is not loaded into your program until you reference it. The autoloaded function can be defined in a file somewhere else and the definition will not appear in your script, allowing you to keep the script small and compact. To use autoload, you need to set the FPATH variable in your ENV file. The FPATH variable contains a search path for directories containing function files. The files in this directory have the same names as the functions defined within them.

The autoload alias for typeset fu specifies that the function names that have not yet been defined are to be autoloaded functions. After the autoload command is executed with the function as its argument, you must invoke the function to execute the commands contained in it. The primary advantage of autoloading functions is better performance, since the Korn shell does not have to read the function definition if it has never been referenced.[9]

Example 10.125
(The Command Line) 1   $ mkdir functionlibrary 2   $ cd functionlibrary 3   $ vi foobar (In Editor) 4   function foobar { pwd; ls; whoami; }  # function has the same                                           # name as the file. (In .profile File) 5   export FPATH=$HOME/functionlibrary    # This path is searched for                                           # functions. (In Your Script) 6   autoload foobar 7   foobar 

EXPLANATION

  1. Make a directory in which to store functions.

  2. Go to the directory.

  3. foobar is a file in functionlibrary. The file foobar contains the definition of function foobar. The filename and function name must match.

  4. The function foobar is defined in the file called foobar.

  5. In the user's .profile initialization file, the FPATH variable is assigned the path where the functions are stored. This is the path the Korn shell will search when autoloading a function. FPATH is exported.

  6. In your script, the function foobar is brought into the program's memory.

  7. The function foobar is invoked.

A number of functions can be stored in one file; for example, calculation functions may be contained in a file called math. Since the function must have the same name as the file in which it is stored, you may create hard links to the function file. Each function name will be a link to the file in which the function is defined. For example, if a function in the math file is called square, use the UNIX ln command to give the math file another name, square. Now the math file and square file can be referenced, and in either case you are referencing the file by the corresponding function name. Now the square function can be autoloaded by its own name.

Example 10.126
(The Command Line) 1   $ ln math square add divide 2   $ ls -i     12256 add     12256 math     12256 square     12256 divide 3   $ autoload square; square 

EXPLANATION

  1. The UNIX ln (link) command lets you give a file alternate names. The math file and square are the same file. The link count is incremented by one for each link created.

  2. A listing shows that all files have the same inode number, meaning they are all one file but can be accessed with different names.

  3. Now, when the square file is autoloaded, the function square has the same name and will be invoked. None of the other functions defined in the file can be referenced until they, in turn, have been specifically autoloaded by name.

10.2.12 Trapping Signals

While your program is running, if you press Control-C or Control-\, the program terminates as soon as the signal arrives. There are times when you would rather not have the program terminate immediately after the signal arrives. You could arrange to ignore the signal and keep running, or perform some sort of cleanup operation before actually exiting the script. The trap command allows you to control the way a program behaves when it receives a signal.

A signal is defined as an asynchronous message that consists of a number that can be sent from one process to another, or by the operating system to a process if certain keys are pressed or if something exceptional happens.[10] The trap command tells the shell to terminate the command currently in execution upon the receipt of a signal. If the trap command is followed by commands within single quotes, those commands will be executed upon receipt of a specified signal. Use the command kill l to get a list of all signals and the numbers corresponding to them.

FORMAT

trap 'command; command' signal 
Example 10.127
trap 'rm tmp*$$; exit 1' 1 2 15 

EXPLANATION

When any of the signals 1 (hangup), 2 (interrupt), or 15 (software termination) arrives, remove all the tmp files and then exit.

If an interrupt comes in while the script is running, the trap command lets you handle the interrupt signal in several ways. You can let the signal behave normally (default), ignore the signal, or create a handler function to be called when the signal arrives. See Table 10.31 for a list of signal numbers and their corresponding names.

Table 10.31. Signals [a] (Type: kill 1)
1) HUP 12) SYS 23) POLL
2) INT 13) PIPE 24) XCPU
3) QUIT 14) ALRM 25) XFSZ
4) ILL 15) TERM 26) VTALRM
5) TRAP 16) URG 27) PROF
6) IOT 17) STOP 28) WINCH
7) EMT 18) TSTP 29) LOST
8) FPE 19) CONT 30) USR1
9) KILL 20) CHLD 31) USR2
10) BUS 21) TTIN  
11) SEGV 22) TTOU  

[a] The output of this command may differ slightly with the operating system.

Pseudo or Fake Signals. The three fake signals are not real signals, but are generated by the shell to help debug a program. They are treated like real signals by the trap command and defined in the same way. See Table 10.32 for a list of pseudo signals.

Table 10.32. Korn Shell Fake Trap Signals
Signal What It Does
DEBUG Executes trap commands after every script command.
ERR Executes trap commands if any command in the script returns a nonzero exit status.
0 or EXIT Executes trap commands if the shell exits.

Signal names such as HUP and INT are normally prefixed with SIG, for example, SIGHUP, SIGINT, and so forth. The Korn shell allows you to use symbolic names for the signals, which are the signal names without the SIG prefix, or you can use the numeric value for the signal. See Example 10.128.

Resetting Signals. To reset a signal to its default behavior, the trap command is followed by the signal name or number. Traps set in functions are local to functions; that is, they are not known outside the function where they were set.

Example 10.128
trap 2  or  trap INT 

EXPLANATION

Resets the default action for signal 2, SIGINT. The default action is to kill the process when the interrupt key (Control-C) is pressed.

Ignoring Signals. If the trap command is followed by a pair of empty quotes, the signals listed will be ignored by the process.

Example 10.129
trap " " 1 2    or    trap "" HUP INT 

EXPLANATION

Signals 1 (SIGHUP) and 2 (SIGINT) will be ignored by the shell process.

Listing Traps. To list all traps and the commands assigned to them, type trap.

Example 10.130
(The Script)     #!/bin/ksh     # Scriptname: trapping     # Script to illustrate the trap command and signals     # Can use the signal numbers or ksh abbreviations seen     # below. Cannot use SIGINT, SIGQUIT, etc. 1   trap 'print "Control C will not terminate $PROGRAM."' INT 2   trap 'print "Control \ will not terminate $PROGRAM."' QUIT 3   trap 'print "Control Z will not terminate $PROGRAM."' TSTP 4   print "Enter any string after the prompt.\     When you are ready to exit, type \"stop\"." 5   while true     do 6      print  n "Go ahead...> " 7      read 8      if [[ $REPLY = [Ss]top ]]        then 9         break        fi 10  done (The Output)     $ trapping 4   Enter any string after the prompt.     When you are ready to exit, type "stop". 6   Go ahead...> this is it^C 1   Control C will not terminate trapping. 6   Go ahead...> this is it again^Z 3   Control Z will not terminate trapping. 6   Go ahead...> this is never it^\ 2   Control \ will not terminate trapping. 6   Go ahead...> stop     $ 

EXPLANATION

  1. The first trap catches the INT signal, Control-C. If Control-C is pressed while the program is running, the command enclosed in quotes will be executed. Instead of aborting, the program will print Control-C will not terminate trapping and continue to prompt the user for input.

  2. The second trap command will be executed when the user presses Control-\, the QUIT signal. The string Control-\ will not terminate trapping will be displayed and the program will continue to run. This signal, SIGQUIT by default, kills the process and produces a core file.

  3. The third trap command will be executed when the user presses Control-Z, the TSTP signal. The string Control-Z will not terminate trapping will be displayed, and the program will continue to run. This signal normally causes the program to be suspended in the background if job control is implemented.

  4. The user is prompted for input.

  5. The while loop is entered.

  6. The string Go ahead > is printed and the program waits for input (see read next line).

  7. The read command assigns user input to the built-in REPLY variable.

  8. If the value of REPLY matches Stop or stop, the break command causes the loop to exit and the program will terminate. Entering Stop or stop is the only way we will get out of this program unless it is killed with the kill command.

  9. The break command causes the body of the loop to be exited.

  10. The done keyword marks the end of the loop.

Example 10.131

graphics/10prfig06.gif

EXPLANATION

  1. The ERR (fake or pseudo) signal will print the message in double quotes any time a command in the program returns a nonzero exit status, that is, fails.

  2. The typeset command with the i option creates an integer variable, number, which can only be assigned integers.

  3. The exit status of the true command is always zero; the body of the while loop is entered.

  4. The user is asked to type in an integer.

  5. The read command reads user input and assigns it to the number variable. The number must be an integer; if not, an error message will be sent to /dev/null. The r option to the read command allows you to enter a negative number (starting with the minus sign).

  6. If the exit status from the read command is zero, a number was entered, and the if statements will be executed.

  7. The break command is executed and the loop exits.

  8. The trap for the fake Korn shell signal ERR is unset.

  9. When grep fails, it returns a nonzero exit status; if we had not unset the ERR trap, the script would have printed You gave me a non-integer. Try again. So long if the grep failed to find ZOMBIE in the /etc/passwd file.

  10. This line is printed if the grep failed. Note that if a floating point number such as 4.5 is entered, the number is truncated to an integer.

Traps and Functions. If trap is used in a function, the trap and its commands are local to the function.

Example 10.132
(The Script)     #!/bin/ksh 1   function trapper {        print "In trapper" 2      trap 'print "Caught in a trap!"' INT        print "Got here."        sleep 25     } 3   while :     do        print "In the main script" 4      trapper   # Call the function 5      print "Still in main"        sleep 5        print "Bye"     done ------------------------------------------------------ (The Output) $ functrap In the main script In trapper Got here. ^CCaught in a trap! $ 

EXPLANATION

  1. The function trapper is defined. It contains the trap command.

  2. The trap command will be executed if Control-C is entered. The print command within the trap is executed and the program continues execution. Control-C is entered while the sleep command is running. Normally, the program will continue to run just after the command where it was interrupted (with the exception of the sleep command, which causes the program to abort). The trap has no effect on lines starting after 4.

  3. In the main part of the script, a while loop is started. The colon is a do-nothing command that always returns a zero exit status. The loop will go forever.

  4. Once in the loop, the function trapper is called.

  5. The trap command within the trapper function will have no effect in this part of the program because the trap is local to the function. If the function exits normally (i.e., ^C is not pressed), execution will continue here. The default behavior for ^C will cause the script to abort if the signal is sent here or to any of the lines that follow.

10.2.13 Coprocesses

A coprocess is a special two-way pipeline that allows shell scripts to write to the standard input of another command and to read from its standard output. This provides a way to create a new interface for an existing program. The append operator, |&, is placed at the end of the command to initiate the command as a coprocess. Normal redirection and background processing should not be used on coprocesses. The print and read commands require a p switch to read from and write to a coprocess. The output must be sent to standard output and have a newline at the end of each message of output. The standard output must be flushed after each message is sent to standard output. You can run multiple coprocesses by using the exec command with the >&p or <&p operator. To open file descriptor 4 as a coprocess, you would enter exec 4>&p.

Example 10.133
(The Script)     #!/bin/ksh     # Scriptname: mycalculator     # A simple calculator   uses the bc command to perform the     # calculations     # Since the shell performs operations on integers only,     # this program allows     # you to use floating point numbers by writing to and reading     # from the bcprogram. 1   cat << EOF     ************************************************** 2        WELCOME TO THE CALCULATOR PROGRAM     ************************************************* 3   EOF 4   bc |&                # Open coprocess 5   while true     do 6       print "Select the letter for one of the operators below " 7       cat <<- EOF             a) +             s)              m) *             d) /             e) ^         EOF 8       read op 9       case $op in             a) op="+";;             s) op=" ";;             m) op="*";;             d) op="/";;             e) op="^";;             *) print "Bad operator"                    continue;;         esac 10      print  p scale=3                     # write to the coprocess 11      print "Please enter two numbers:  "  # write to standard out 12      read num1 num2                       # read from standard in 13      print  p "$num1" "$op" "$num2"       # write to the coprocess 14      read  p result                       # read from the coprocess 15      print $result 16      print  n "Continue (y/n)? " 17      read answer 18      case $answer in         [Nn]* ) 19         break;;         esac 20  done 21  print Good bye (The Output) $ mycalculator     ************************************************** 1        WELCOME TO THE CALCULATOR PROGRAM     ************************************************* 6   Select one of the operators below 7        a) +          s)            m) *          d) /          e) ^     e 11  Please enter two numbers:     2.3 4     27.984 16  Continue (y/n)? y 6   Select one of the operators below 7        a) +          s)            m) *          d) /          e) ^     d 11  Please enter two numbers:     2.1 4.6     0.456 16  Continue (y/n)? y 6   Select one of the operators below 7       a) +         s)           m) *         d) /         e) ^     m 11  Please enter two numbers:     4 5     20 16  Continue (y/n)? n     Good bye 

EXPLANATION

  1. The here document is used to display a menu.

  2. This text is printed as a header to the menu below.

  3. EOF is a user-defined terminator, marking the end of the here document.

  4. The bc command (desk calculator) is opened as a coprocess. It is executed in the background.

  5. A while loop is started. Since the true command always returns a successful exit status of 0, the loop will continue indefinitely until a break or exit is reached.

  6. The user is prompted to select an item from a menu to be displayed.

  7. Another here document displays a list of math operations the user can choose for the bc program.

  8. The read command assigns user input to the variable op.

  9. The case command matches for one of the op values and assigns an operator to op.

  10. The print command, with the p option, pipes output, scale=3, to the coprocess, the bc command. The bc command accepts the print output as input and sets the scale to 3. (The scale defines the number of significant digits to the right of the decimal point in a number that will be displayed by bc. )

  11. The user is prompted to enter two numbers.

  12. The read command assigns user input to the variables num1 and num2.

  13. The print p command sends the arithmetic expression to the bc coprocess.

  14. The shell reads from the bc coprocess (read p) and assigns the input to the variable result.

  15. The result of the calculation ($result) is printed.

  16. The user is asked about continuing.

  17. The user enters input. It is assigned to the variable answer.

  18. The case command evaluates the variable answer.

  19. If the user had entered No or no or nope, etc., the break command would be executed, and the while loop would be terminated with control sent to line 21.

  20. The done keyword marks the end of the while loop.

  21. This line is printed when the loop terminates.

10.2.14 Debugging

By turning on the noexec option or using the n argument to the ksh command, you can check the syntax of your scripts without really executing any of the commands. If there is a syntax error in the script, the shell will report the error. If there are no errors, nothing is displayed.

The most commonly used method for debugging scripts is to turn on the xtrace option or to use the ksh command with the x option. These options allow an execution trace of your script. Each command from your script is displayed after variable substitution has been performed, and then the command is executed. When a line from your script is displayed, it is preceded with the value of the PS4 prompt, a plus (+) sign. The PS4 prompt can be changed.

With the verbose option turned on, or by invoking the Korn shell with the v option (ksh v scriptname), each line of the script will be displayed, just as it was typed in the script, and then executed. See Table 10.33 for debug commands.

Table 10.33. Debug Commands and Options
Command Function How It Works
ksh x scriptname Invokes ksh with echo option. Displays each line of the script after variable substitution and before execution.
ksh v scriptname Invokes ksh with verbose option. Displays each line of the script before execution, just as you typed it.
ksh n scriptname Invokes ksh with noexec option. Interprets but does not execute commands.
set x or Turns on echo option. Traces execution in a script.
set o xtrace    
set +x Turns off echo. Turns off tracing.
typeset ft Turns on tracing. Traces execution in a function.
export PS4='$LINENO ' The PS4 prompt by default is a +. You can reset the prompt. In this example, a line number will be printed for each line.
trap 'print $LINENO ' DEBUG Prints value of $LINENO for each line in the script. For each script command, the trap action is performed. See format for trap.
trap 'print Bad input' ERR   If a nonzero exit status is returned, the trap is executed.
trap 'print Exiting from $0' EXIT   Prints message when script or function exits.
Example 10.134
(The Script)     #!/bin/ksh     # Scriptname: todebug 1   name="Joe Blow" 2   if [[ $name = [Jj]* ]] then         print Hi $name     fi     num=1 3   while (( num < 5 ))     do 4       (( num=num+1 ))     done 5   print The grand total is $num (The Output) 1   $ ksh  x todebug 2   + name=Joe Blow     + [[ Joe Blow = [Jj]* ]]     + print Hi Joe Blow     Hi Joe Blow     + num=1         The + is the PS4 prompt     + let num < 5     + let num=num+1     + let num < 5     + let num=num+1     + let num < 5     + let num=num+1     + let num < 5     + let num=num+1     + let num < 5     + print The grand total is 5     The grand total is 5 

EXPLANATION

  1. The Korn shell is invoked with the x option. Echoing is turned on. Each line of the script will be displayed on the screen, followed by the result of executing that line. Variable substitution is performed. Alternatively, the x option can be used in the script instead of at the command line; e.g., #!/bin/ksh x

  2. The lines are preceded by the plus (+) sign, the PS4 prompt.

  3. The while loop is entered. It will loop 4 times.

  4. The value of num is incremented by by 1.

  5. After the while loop exits, this line is printed.

Example 10.135
(The Script)     #!/bin/ksh     # Scriptname: todebug2 1   trap 'print "num=$num on line $LINENO"' DEBUG     num=1     while (( num < 5 ))     do         (( num=num+1 ))     done     print The grand total is $num (The Output)     $ todebug2 2   num=1 on line 3     num=1 on line 4     num=2 on line 6     num=2 on line 4     num=3 on line 6     num=3 on line 4     num=4 on line 6     num=4 on line 4     num=5 on line 6     num=5 on line 4     The grand total is 5     num=5 on line 8     num=5 on line 8 

EXPLANATION

  1. LINENO is a special Korn shell variable that holds the number of the current script line. The DEBUG signal, used with the trap command, causes the string enclosed in single quotes to be executed every time a command in the script is executed.

  2. As the while loop executes, the value of the variable num and the line of the script are displayed.

10.2.15 Processing Command Line Options with getopts

If you are writing scripts that require a number of command line options, positional parameters are not always most efficient. For example, the UNIX ls command takes a number of command line options and arguments. (An option requires a leading dash; an argument does not.) Options can be passed to the program in several ways: ls laFi, ls i a l F, ls ia F, and so forth. If you have a script that requires arguments, positional parameters might be used to process the arguments individually, such as ls l i F. Each dash option would be stored in $1, $2, and $3, respectively. But, what if the user listed all of the options as one dash option, as in ls liF? Now the liF would all be assigned to $1 in the script. The getopts function makes it possible to process command line options and arguments in the same way they are processed by the ls program.[11] The getopts function will allow the runit program to process its arguments using a variety of combinations.

Example 10.136
(The Command Line) 1   $ runit  x  n 200 filex 2   $ runit  xn200 filex 3   $ runit  xy 4   $ runit  yx  n 30 5   $ runit  n250  xy filey (any other combination of these arguments ) 

EXPLANATION

  1. The program runit takes four arguments; x is an option, n is an option requiring a number argument after it, and filex is an argument that stands alone.

  2. The program runit combines the options x and n and the number argument 200; filex is also an argument.

  3. The program runit combines the x and y options.

  4. The program runit combines the y and x options; the n option is passed separately as is the number argument, 30.

  5. The program runit combines the n option with the number argument; the x and y options are combined and the filey is separate.

Before getting into all the details of the runit program, we examine the line from the program where getopts is used to see how it processes the arguments. The following is a line from the script called runit:

while getopts :xyn: name 
  1. x, y, and n are the options.

  2. Options typed at the command line begin with either or +.

  3. Any options that do not contain a + or tell getopts that the option list is at an end.

  4. The colon after an option says that the option requires an argument; that is, the n option requires an argument.

  5. The colon before an option list says that if you type an illegal option, getopts will allow the programmer to handle it. For example, in the command runit p, where p is not one of the legal options, getopts will tell you so programmatically. The shell does not print an error message.

  6. Each time getopts is called, it places the next option it finds, without the dash, in the variable name. (You can use any variable name here.) If there is a plus sign prepended to the option, then it goes into name with the plus sign. If an illegal argument is given, name is assigned a question mark; if a required argument is missing, name is assigned a colon.

  7. OPTIND is a special variable that is initialized to one and is incremented each time getopts completes processing a command line argument to the number of the next argument getopts will process.

  8. The OPTARG variable contains the value of a legal argument, or if an illegal option is given, the value of the illegal option is stored in OPTARG.

Sample getopts Scripts. The following sample scripts illustrate how getopts processes arguments.

Example 10.137
(The Script)     #!/bin/ksh     # Program opts1     # Using getopts   First try   1   while getopts xy options     do 2   case $options in 3       x) print "you entered  x as an option";;         y) print "you entered  y as an option";;     esac     done ----------------------------------------------------------------- (The Command Line) 4   $ opts1  x     you entered  x as an option 5   $ opts1  xy     you entered  x as an option     you entered  y as an option 6   $ opts1  y     you entered  y as an option 7   $ opts1  b     opts1[3]: getopts: b bad option(s) 8   $ opts1 b 

EXPLANATION

  1. The getopts command is used as a condition for the while command. The valid options for this program are listed after the getopts command; they are x and y. Each option is tested in the body of the loop, one after the other. Each option will be assigned to the variable options, without the leading dash. When there are no longer any arguments to process, getopts will exit with a nonzero status, causing the while loop to terminate.

  2. The case command is used to test each of the possible options found in the options variable, either x or y.

  3. If x was an option, the string you entered x as an option is displayed.

  4. At the command line, the opts1 script is given an x option, a legal option to be processed by getopts.

  5. At the command line, the opts1 script is given an xy option, legal options to be processed by getopts.

  6. At the command line, the opts1 script is given a y option, a legal option to be processed by getopts.

  7. The opts1 script is given a b option, an illegal option. Getopts sends an error message.

  8. An option without a or + prepended to it is not an option and causes getopts to stop processing arguments.

Example 10.138
(The Script)     #!/bin/ksh     # Program opts2     # Using getopts   Second try   1   while getopts :xy options     do 2        case $options in          x) print "you entered  x as an option";;          y) print "you entered  y as an option";; 3        \?) print $OPTARG is not a valid option 1>&2;;          esac     done ------------------------------------------------------------------ (The Command Line)     $ opts2  x     you entered  x as an option     $ opts2  y     you entered  y as an option     $ opts2 xy     $ opts2  xy     you entered  x as an option     you entered  y as an option 4   $ opts2  g     g is not a valid option 5   $ opts2  c     c is not a valid option 

EXPLANATION

  1. The colon preceding the option list prevents the Korn shell from printing an error message for a bad option. However, if the option is a bad option, a question mark will be assigned to the options variable.

  2. The case command can be used to test for the question mark, allowing you to print your own error message to standard error.

  3. If the options variable is assigned the question mark, this case statement is executed. The question mark is protected with the backslash so that the Korn shell does not see it as a wildcard and try to perform filename substitution.

  4. g is not a legal option. The question mark is assigned to the options variable, and OPTARG is assigned the illegal option g.

  5. c is not a legal option. The question mark is assigned to the options variable, and OPTARG is assigned the illegal option c.

Example 10.139
(The Script)     #!/bin/ksh     # Program opts3     # Using getopts   Third try   1   while getopts :d options     do         case $options in 2       d) print  R " d is the ON switch";; 3       +d) print  R "+d is the OFF switch";;         \?) print $OPTARG is not a valid option;;         esac     done  # Need the  R option with print or the shell tries to use  d as a  # print option ------------------------------------------------------------------ (The Command Line) 4   $ opts3  d      d is the ON switch 5   $ opts3 +d     +d is the OFF switch 6   $ opts3  e     e is not a valid option 7   $ opts3 e 

EXPLANATION

  1. The while command tests the exit status of getopts; if getopts can successfully process an argument, it returns zero exit status, and the body of the while loop is entered. The colon prepended to the d option tells getopts not to print an error message if the user enters an invalid option.

  2. One of the legal options is d. If d is entered as an option, the d (without the dash) is stored in the options variable. (The R option to the print command allows the first character in the print string to be a dash.)

  3. One of the legal options is +d. If +d is entered as an option, the d (with the plus sign) is stored in the options variable.

  4. The d option is a legal option to opts3.

  5. The +d option is also a legal option to opts3.

  6. The e option is invalid. A question mark is stored in options if the option is illegal. The illegal argument is stored in OPTARG.

  7. The option is prepended with neither a dash nor a plus sign. The getopts command will not process it as an option and returns a nonzero exit status. The while loop is terminated.

Example 10.140
(The Script)     #!/bin/ksh     # Program opts4     # Using getopts   Fourth try   1   alias USAGE='print "usage: opts4 [-x] filename " >&2' 2   while getopts :x: arguments     do     case $arguments in 3        x) print "$OPTARG is the name of the argument ";; 4        :) print "Please enter an argument after the -x option" >&2            USAGE ;; 5       \?) print "$OPTARG is not a valid option." >&2            USAGE;;     esac 6   print "$OPTIND" # The number of the next argument to be processed     done ----------------------------------------------------------------- (The Command Line) 7   $ opts4 -x     Please enter an argument after the -x option     usage: opts4 [-x] filename     2 8   $ opts4 -x filex     filex is the name of the argument     3 9   $ opts4 -d     d is not a valid option.     usage: opts4 [-x] filename     1 

EXPLANATION

  1. The alias USAGE is assigned the diagnostic error message that will be printed if getopts fails.

  2. The while command tests the exit status of getopts; if getopts can successfully process an argument, it returns zero exit status, and the body of the while loop is entered. The colon prepended to the x option tells getopts not to print an error message if the user enters an invalid option. The colon appended to the x option tells getopts that an argument should follow the x option. If the option takes an argument, the argument is stored in the getopts built-in variable, OPTARG.

  3. If the x option was given an argument, the argument is stored in the OPTARG variable and will be printed.

  4. If an argument was not provided after the x option, a colon is stored in the variable arguments. The appropriate error message is displayed.

  5. If an invalid option is entered, the question mark is stored in the variable arguments and an error message is displayed.

  6. The special getopts variable, OPTIND, holds the number of the next option to be processed. Its value is always one more than the actual number of command line arguments.

  7. The x option requires an argument. An error message is printed.

  8. The name of the argument is filex. The variable OPTARG holds the name of the argument filex.

  9. The option d is invalid. The usage message is displayed.

10.2.16 Security

Privileged Scripts. A script is privileged if the Korn shell is invoked with the p option. When the privileged option is used and the real UID and/or the real GID are not the same as the effective UID or effective GID, the .profile will not be executed and a system file called /etc/suid_profile will be executed instead of the ENV file.

Restricted Shells. When the Korn shell is invoked with the r option, the shell is restricted. When the shell is restricted, the cd command cannot be used and the SHELL, ENV, and PATH variables cannot be modified or unset; commands cannot be executed if the first character is a backslash; and the redirection operators (>, <, |, >>) are illegal. This option cannot be unset or set with the set command. The command rksh will invoke a restricted shell.

10.2.17 Built-In Commands

The Korn shell has a number of built-in commands, as shown in Table 10.34.

Table 10.34. Built-In Commands and Their Functions
Command Function
: Do-nothing command; returns exit status zero.
.file The dot command reads and executes a command from file.
break See looping.
continue See looping.
cd Changes directory.
echo [ args ] Displays arguments.
eval command Shell scans the command line twice before execution.
exec command Runs command in place of this shell.
exit [ n ] Exit the shell with status n.
export [ var ] Makes var known to subshells.
fc e [ editor ] [ lnr ] first last Used to edit commands in the history list. If no editor is specified, the value of FCEDIT is used; if FCEDIT is not set, /bin/ed is used. Usually history is aliased to fc l.
  Examples:
fc l Lists the last 16 commands on the history list.
fc e emacs grep Reads the last grep command into the emacs editor.
fc 25 30 Reads commands 25 through 30 into the editor specified in FCEDIT, by default the ed editor.
fc e Reexecutes the last command.
fc e Tom=Joe 28 Replaces Tom with Joe in history command 28.
fg Brings the last background job to the foreground.
fg %n Brings job number n to the foreground. Type jobs to find the correct job number.
jobs [ l] Lists the active jobs by number and with the l option by PID number.
  Examples:
$ jobs
[3] + Running sleep 50&
[1] Stopped vi
[2] Running sleep%
kill [ signal process ] Sends the signal to the PID number or job number of process. See /usr/include/sys/signal.h for a list of signals.
  Signals:
SIGHUP1 /* hangup (disconnect) */
SIGINT 2 /* interrupt */
SIGQUIT 3 /* quit */
SIGILL 4 /* illegal instruction (not reset when caught) */
SIGTRAP 5 /* trace trap (not reset when caught) */
SIGIOT 6 /* IOT instruction */
SIGABRT 6 /* used by abort, replace SIGIOT in the future */
SIGEMT 7 /* EMT instruction */
SIGFPE 8 /* floating point exception */
SIGKILL 9 /* kill (cannot be caught or ignored) */
SIGBUS 10 /* bus error */
SIGSEGV 11 /* segmentation violation */
SIGSYS 12 /* bad argument to system call */
SIGPIPE 13 /* write on a pipe with no one to read it */
SIGALRM 14 /* alarm clock */
SIGTERM 15 /* software termination signal from kill */
SIGURG 16 /* urgent condition on I/O channel */
SIGSTOP 17 /* sendable stop signal not from tty */
SIGTSTP 18 /* stop signal from tty */
SIGCONT 19 /* continue a stopped process */
(To use the kill command and a signal name, strip off the SIG prefix and precede the signal name with a dash.)
Examples:
kill INT %3
kill HUP 1256
kill 9 %3
kill %1
getopts Used in shell scripts to parse command line and check for legal options.
hash Lists all tracked aliases.
login [ username ]  
newgrp [ arg ] Changes your real group ID to the group ID.
print [nrRsup] Replacement for echo. See print.
pwd Print present working directory.
read [ var ] Read line from standard input into variable var.
readonly [ var ] Make variable var readonly. Cannot be reset.
return [ n ] Exit value given to a function.
set [ aefhknoptuvx [ o option] [ A arrayname] [ arg ] ]
  Examples:
set Lists all variables and their values.
set + Lists all variables without their values.
set o Lists all option settings.
set a b c Resets positional parameters $1, $2, $3.
set s Sorts $1, $2, and $3 alphabetically.
set o vi Sets the vi option.
set xv Turns on the xtrace and verbose options for debugging.
set Unsets all positional parameters.
set "$x" Sets $1 to the value of x, even if x is x.
set == $x Does pathname expansion on each item in x and then sets the positional parameters to each item.
set A name tom dick harry name[0] is set to tom.
name[1] is set to dick.
name[2] is set to harry.
set +A name joe name[0] is reset to joe, the rest of the array is left alone.
name[1] is dick.
name[2] is harry.
(To set options, use the o flag; to unset options, use the +o flag.)
Example:
set o ignoreeof
Options:
allexport After setting this, exports any variable defined or changed.
bgnice Runs background jobs with a lesser priority; used instead of nice.
emacs Sets the emacs built-in editor.
errexit The shell exits when a command returns a nonzero exit status.
gmacs Sets the built-in gmacs editor.
ignoreeof Ignores the EOF (Control-D) key from terminating the shell. Must use exit to exit.
keyword Adds keyword arguments occurring anywhere on the command line to the environment of the shell.
markdirs Puts a trailing backslash on all directory names resulting from filename expansion.
monitor Sets job control.
noclobber Prevents overwriting files using the redirection operator, >. Use >| to force overwrite.
noexec Same as ksh n; reads commands but does not execute them. Used to check for syntax errors in shell scripts.
noglob Disables pathname expansion with ksh wildcard metacharacters.
nolog Function definitions will not be stored in the history file.
nounset Displays an error if a variable has not been set.
privileged Turns on privileged mode for setuid programs.
trackall Ksh causes each command to become a tracked alias; automatically turned on for interactive shells.
verbose Echos each line of input to standard error; useful in debugging.
vi Sets the vi built-in editor.
viraw Specifies vi character at a time input.
xtrace Expands each command and displays it in the PS4 prompt, with variables expanded.
shift [ n ] Shifts positional parameters to the left n times.
times Prints accumulated user and system times for processes run from this shell.
trap [ arg ] [ n ] When shell receives signal n ( 0, 1, 2, or 15 ), arg is executed.
type [ command ] Prints the type of command; e.g., pwd is a built-in shell. In ksh, an alias for whence v.
typeset [ options ] [ var ] Sets attributes and values for shell variables and functions.
ulimit [ options size ] Sets maximum limits on processes.
Examples:
ulimit a Display all limits:
Time (seconds) unlimited.
File (blocks) unlimited.
Data (kbytes) 524280.
Stack (kbytes) 8192.
Memory (kbytes) unlimited.
Coredump (blocks) unlimited.
Other Options:
c size Limits core dumps to size blocks.
d size Limits the data size (of executables) to size blocks.
f size Limits the size of files to size blocks (default).
m size Limits the size of physical memory to size K bytes.
s size Limits the size of the stack area to size K bytes.
t secs Limits process execution time to secs seconds.
umask [ mask ] Without argument, prints out file creation mask for permissions.
umask [ octal digits ] User file creation mode mask for owner, group, and others.
unset [ name ] Unsets value of variable or function.
wait [ pid#n ] Waits for background process with PID number n and report termination status.
whence [ command ] Prints information about the command, like ucb whereis.
Examples:
whence v happy happy is a function
whence v addon addon is an undefined function
whence v ls ls is a tracked alias for /bin/ls
whence ls /bin/ls

10.2.18 Korn Shell Invocation Arguments

When the Korn shell is involved, it can take options to control its behavior. See Table 10.35.

Table 10.35. Arguments to ksh
Command Function
a Automatically exports all variables.
c cmd Executes a command string.
e Exits when a command returns a nonzero status.
f Turns off globbing, the expansion of filename metacharacters.
h Causes commands to be treated as tracked aliases.
i Sets the interactive mode.
k Sets the keyword option. All the key arguments to commands will be made part of the environment.
m Causes commands executed in the background to be run in a separate process group, and will continue to run even if Control-C or logout is attempted. Sends a message that the job has terminated when done.
n Can be used for debugging. Commands are scanned, but not executed. Can be used with x and v options.
o Allows options to be set by the names listed in the table above with the set command.
p Turns on privileged mode. Used for running setuid programs.
r Sets the restricted mode.
s Reads command from stdin, the default.
t Causes the shell to exit after executing the first command found in shell input and the c option is specified.
u Any reference to an unset variable is considered an error.
v Each line of a script or standard input is printed before any parsing, variable substitution, or other processing is performed. Output is written to standard error. Used for debugging.
x Each line of a script or standard input is printed before it is executed. Filename expansion, variable substitution, and command substitution are shown in the output. All output is prepended with the value of the PS4 prompt, a plus sign followed by a space. Lines are written to standard error.

KORN SHELL LAB EXERCISES

Lab 28: Getting Started

1:

What shell are you using? How do you know?

2:

Do you have a .profile and/or a .kshrc file in your home directory? What is the difference between the .profile and .kshrc? What is the ENV file and how can you invoke it if you make changes in it?

3:

What is the default primary prompt? What is the default secondary prompt? Change your primary prompt at the command line so that it contains your login name.

4:

What is the purpose of setting each of the following variables?

  1. set o ignoreeof

  2. set o noclobber

  3. set o trackall

  4. set o monitor

  5. set o vi

Why are these variables set in the ENV file? What is the purpose of the PATH? What are the elements of your PATH variable?

5:

What is the difference between a local and an environment variable? How do you list all your variables? How do you list only environment variables? To list all your current option settings, type the following:

set  o 

Which set options are turned on?

6:

Create a local variable called myname that contains your full name. Now export the variable. Type the following at the prompt:

ksh 

Was the variable name exported? Type exit to get back to the parent shell. Make the variable name readonly. What is a readonly variable?

7:

What are positional parameters normally used for? Type the following:

set apples pears peaches plums 

Using the positional parameters, print plums. Print apples peaches. Print apples pears peaches plums. Print the number of parameters. Reset the positional parameters to a list of veggies. Print the whole list of veggies. What happened to the fruit list?

Type the following:

set  print $* 

What happened?

8:

Print the PID of the current shell. Type the following at the prompt:

grep $LOGNAME /etc/passwd echo $? 

What does the $? tell you. What does the exit status tell you about the execution of a command?

9:

Change both the primary and secondary prompt in your .profile. How do you reexecute the .profile file without logging out and logging back in?

Lab 29: History

1:

What is your HISTSIZE variable set to? What is your HISTFILE variable set to? Check your .kshrc file to see if set o vi is there. If it has not been set, set it in the .kshrc file and reexecute the file by typing the following:

. .kshrc 
2:

Type the following commands at the command line:

ls date who cal 2 1993 date +%T 

Type history or fc l. What do these commands do? Print your history list in reverse. Print your history list without numbers. Print the current command and the five preceding it. Print everything from the tenth command to the present. Print everything between the most recent ls command to the most recent cal command.

3:

Using the r command, reexecute the last command. Reexecute the last command that started with the letter d. Change the cal command year output to 1897. Change the date command +%T argument to find the current hour.

4:

If your history is set, press the Esc key at the command line and use the K key to move up through the history list. Change the ls command to ls alF and reexecute it.

5:

Check to see if the FCEDIT variable has been set by typing the env command. If it has not been set, type the following at the command line:

export FCEDIT=vi 

Now type the following at the command line:

fc  1  4 

What happened?

6:

How do you comment a line from your history list, so that it will be placed on the list without being executed?

7:

At the command line, type the following:

touch a1 a2 a3 apples bears balloons a4 a45 

Now using the history Esc sequences shown in Tables 10.2 and 10.3, print all the files beginning with an a.

  1. Print the first file beginning with a.

  2. Print a list of all files beginning with a.

  3. Print the first file beginning with b.

  4. Print a command and comment it.

8:

At the command line, type the following:

print a b c d e 
9:

Using the history Esc underscore command, change the command to the following:

print e 

Using the history Esc underscore command, change the first command to output:

print c 

Lab 30: Aliases and Functions

1:

What command lists all the aliases currently set?

2:

What command lists all the tracked aliases?

3:

Create aliases for the following commands:

date +%T

history n

ls alF

rm i

cp i

print

4:

How do you export an alias?

5:

Create a function that contains the following commands:

ls F

print n "The time is"

date +%T

print n "Your present working directory is"

pwd

6:

Execute the function.

7:

Now create your own functions, using positional parameters to pass arguments.

8:

What command lists the functions and their definitions?

9:

Try some of the print options.

Lab 31: Shell Metacharacters

1:

Create a directory called meta. Cd to that directory. Use touch to create the following files:

abc abc1 abc2 abc2191 Abc1 ab2 ab3 ab345 abc29 abc9 abc91 abc21xyz abc2121 noone nobody nothing nowhere 
2:

Do the following:

  1. List all files that start with a lower case a.

  2. List all files starting with upper case A followed by two characters.

  3. List all files that end in a number.

  4. List all files that match one number after abc.

  5. List all files that match nothing or noone.

  6. List all files that match one or more numbers after abc.

  7. List all files that do not contain the pattern abc.

  8. List all files that contain ab followed by a 3 or 4.

  9. List all files starting with a or A, followed by b, and ending in one number.

  10. What is the error message if there is not a match?

Lab 32: Tilde Expansion, Quotes, and Command Substitution

1:

Use the tilde to do the following:

  1. Print your home directory.

  2. Print your neighbor's home directory.

  3. Print your previous working directory.

  4. Print your current working directory.

2:

What variable holds the value of your present working directory? What variable holds the value of your previous working directory?

3:

Use the to go to your previous working directory.

4:

Use the print command to send the following output to the screen. (The word enclosed in < > is a variable name that will be expanded, and words enclosed in [ ] are output of commands that have been executed; i.e., use command substitution.)

Hi <LOGNAME> how's your day going? "No, <LOGNAME> you can't use the car tonight!", she cried. The time is [ Sun Feb 21 13:19:27 PST 2001 ] The name of this machine is [ eagle ] and the time is [ 31:19:27 ] 
5:

Create a file that contains a list of user names. Now create a variable called nlist which contains the list of user names, extracted by using command substitution.

  1. Print out the value of the variable. How does command substitution affect the formatting of a list?

  2. Test this by setting a variable to the output of the ps eaf command.

  3. What happened to the formatting?

Lab 33: Redirection

1:

Go into the editor and create the following two-line text file called ex6:

Last time I went to the beach I found a sea shell. While in Kansas I found a corn shell. 
2:

Now append this line to your ex6 file: The National Enquirer says someone gave birth to a shell, called the born shell.

3:

Mail the ex6 file to yourself.

4:

Using a pipe, count the number of lines (wc l) in your ex6 file.

5:

To list all set options, type the following:

set  o 

Do you have the noclobber variable set? If not, type the following:

set  o noclobber 

What happened?

6:

Type the following at the command line:

cat << FINIS How are you $LOGNAME The time is 'date'Bye!! FINIS 

What printed?

7:

Now try this using tabs:

cat <<  END         hello there         how are you END 

What printed?

8:

Type the following at the command line:

kat file 2> error || print kat failed 

What happened? Why?

9:

Now type the following at the command line:

cat zombie 2> errorfile || print cat failed 

What happened? Why? How does the && operator work? Try your own command to test it.

10:

Use the find command to print all files that begin with an a from the root directory down. Put the standard output in a file called foundit and send the errors to /dev/null.

Lab 34: Job Control

1:

At the command line type the following:

mail <user>Press control z 

Now type:

jobs 

What is the number in the square brackets?

2:

Now type:

sleep 300 jobs bg 

What does bg do? What do the + and signs indicate?

3:

Kill the mail job using job control.

4:

Go into the editor. Type ^Z to stop the job.

Now bring the stopped vi job back into the foreground. What command did you type?

5:

Type the following command:

jobs  l 

What is the output?

6:

What is the TMOUT variable used for?

7:

How much time was spent by the kernel when executing the following command:

(sleep 5 ; ps  eaf ) 

Lab 35: Writing the info Shell Script

1:

Write a program called info. Make sure you make the program executable with the chmod command before you try to execute it.

2:

The program should contain comments.

3:

The program should do the following when executed:

  1. Output the number of users logged on.

  2. Output the time and date.

  3. Output the present working directory.

  4. List all directory files in the parent directory.

  5. Print out the name of the shell being used.

  6. Print a line from the password file containing your login name.

  7. Print your user ID.

  8. Print the name of this machine.

  9. Print your disk usage.

  10. Print a calendar for this month.

  11. Tell the user good bye and print the hour in nonmilitary time.

Lab 36: Variable Expansion of Substrings

1:

Write a script that will do the following:

  1. Set a variable called mypath to your home directory.

  2. Print the value of mypath.

  3. Print just the last element of the path in mypath.

  4. Print the first element of the path in mypath.

  5. Print all but the last element of the variable mypath.

Lab 37: The lookup Script

1:

Create a file called datafile if it has not been provided for you on the CD. It will consist of colon-separated fields:

  1. First and last name

  2. Phone number

  3. Address (street, city, state, and zip)

  4. Birth date (04/12/66)

  5. Salary

2:

Put 10 entries in your file. Write a script called lookup that will do the following:

  1. Welcome the user.

  2. Print the names and phone numbers for all the users in the datafile.

  3. Print the number of lines in the datafile.

  4. Tell the user good bye.

Lab 38: Using typeset

1:

Write a script that will do the following:

  1. Ask the user to type in his or her first and last name.

  2. Store the answers in two variables.

  3. Use the new ksh read command.

2:

Use the typeset command to convert the first and last name variables to all lowercase letters.

3:

Test to see if the person's name is tom jones. If it is, print Welcome, Tom Jones; if it is not, print, Are you happy today, FIRSTNAME LASTNAME?. (The user's first and last names are converted to uppercase letters.)

4:

Have the user type in an answer to the question and use the new ksh test command to see whether the answer is yes or no. If yes, have your script say something nice to him or her, and if no, tell the user to go home and give the current time of day.

5:

Rewrite the lookup script.

  1. The script will ask the user if he or she would like to add an entry to the datafile.

  2. If the user answers yes or y, ask for the following input:

    Name

    Phone number

    Address

    Birth date

    Salary

A variable for each item will be assigned the user input.

print  n "What is the name of the person you are adding to the file?" read name The information will be appended to the datafile. 

Lab 39: The if/else Construct and the let Command

1:

Write a script called grades that will ask the user for his or her numeric grade on a test.

  1. The script will test that the grade is within the possible grade range, 0 to 100.

  2. The script will tell the user if he or she got an A, B, C, D, or F.

2:

Write a script called calc that will perform the functions of a simple calculator. The script will provide a simple menu:


[a] Add
[s] Subtract
[m] Multiply
[d] Divide
[r] Remainder
 

3:

The user will choose one of the letters from the menu.

4:

The user will then be asked to enter two integers between 0 and 100.

5:

If the numbers are out of the range, an error message will be printed and the script will exit.

6:

The program will perform the arithmetic on the two integers.

7:

The answer will be printed in base 10, 8, and 16.

Lab 40: The case Statement

1:

Write a script timegreet that will do the following:

  1. Provide a comment section at the top of the script, with your name, the date, and the purpose of this program.

  2. Convert the following program using case statements:

    # The timegreet script by Ellie Quigley you=$LOGNAME hour='date | awk '{print substr($4, 1, 2)}'' print "The time is: $(date)" if (( hour > 0 && $hour < 12 )) then    print "Good morning, $you!" elif (( hour == 12 )) then    print "Lunch time!" elif (( hour > 12 && $hour < 16 )) then    print "Good afternoon, $you!" else    print "Good night, $you!" fi 
2:

Rewrite the lookup script, replacing the if/elif construct with the case command. Add one more menu item:


1) Add Entry
2) Delete Entry
3) Update Entry
4) View Entry
5) Exit
 

Lab 41: The select Loop

1:

Write a script that will do the following:

  1. Provide a comment section at the top of the script, with your name, the date, and the purpose of this program.

  2. Use the select loop to provide a menu of foods. The output will resemble the following:

    $ foods 1) steak and potatoes 2) fish and chips 3) soup and salad Please make a selection. 1 Stick to your ribs Watch your cholesterol Enjoy your meal. $ foods 1) steak and potatoes 2) fish and chips 3) soup and salad Please make a selection. 2 British are coming Enjoy your meal. $ foods 1) steak and potatoes 2) fish and chips 3) soup and salad Please make a selection. 3 Health foods Dieting is so boring. Enjoy your meal. $ foods 1) steak and potatoes 2) fish and chips 3) soup and salad Please make a selection. 5 Not on the menu today! 
2:

Rewrite the lookup script using the select command to create a main menu and a sub menu. The menu will resemble the following:

1) Add Entry 2) Delete Entry 3) Update Entry 4) View Entry      a) Name      b) Phone      c) Address      d) Birthday      e) Salary 5) Exit 

Lab 42: Autoloading Functions

Steps for autoloading a function:

1:

Make a directory called myfunctions.

2:

Change directory to myfunctions and use the editor to create a file called goodbye.

3:

Insert in the goodbye file a function called goodbye, spelled exactly the same as the filename.

4:

The goodbye function contains:

function goodbye { print The current time is $(date) print "The name of this script is $0" print See you later $1 print Your machine is 'uname  n' } 
5:

Write and quit the editor. You now have a file containing a function with the same name.

6:

Go to your home directory. Modify the .kshrc file in the editor by typing the following line:

FPATH=$HOME/myfunctions 
7:

Exit the editor, and to execute the .kshrc in the current environment, use the dot command.

8:

In the timegreet script you have already written, include the following lines:

autoload goodbye goodbye $LOGNAME 
9:

Run the timegreet script. The goodbye function output will appear.

10:

Create functions for each of the menu items in the lookup script. Store the functions in a file called lookup_functions in a directory called myfunctions.

11:

Autoload the functions in your lookup script and make the function calls for the corresponding cases.

12:

Use the trap command so that if the user enters a menu selection other than an integer value, the trap command will print an error to the screen, and cause the script to ask the user to reenter the correct data type.

[1]  A built-in command will override a function; therefore, an alias must be defined to the name of the function. (See "Aliases".) In the 1994 version of the Korn shell, the order of processing functions and built-ins was reversed, thus alleviating this problem.

[2]  On versions of the Korn shell newer than 1988, the FCEDIT variable has been renamed HISTEDIT, and the fc command has been renamed hist.

[3]  Tracked aliases will be undefined if the PATH variable is reset.

[4]  Using backquotes for command substitution is an old form still used in the Bourne and C shells. Although still legal syntax, the Korn shell introduces a new method shown in this section.

[5]  The POSIX standard defines functions with the Bourne shell syntax, but variables and traps cannot be local in scope, as with the new Korn shell definition.

[6]  Bases greater than 36 are available on versions of the Korn shell that are newer than 1988.

[7]  If you want the menu to reappear when the loop starts again, set the REPLY variable to null just before the done keyword.

[8]   Korn, David G., and Bolsky, Morris I., The Korn Shell Command and Programming Language (Englewood Cliffs, NJ: Prentice-Hall, Inc., 1988), p. 77.

[9]  Bolsky, Morris, and Korn, David, The New Kornshell. (Upper Saddle River, NJ: Prentice Hall, 1995), p. 78.

[10]  Bolsky, Morris I., and Korn, David G., The New KornShell Command and Programming Language (Englewood Cliffs, NJ: Prentice Hall PTR, 1995), p. 327.

[11]  See Section 3 of the UNIX manual for the C library function getopt.

CONTENTS


UNIX Shells by Example
UNIX Shells by Example, 3rd Edition
ISBN: 013066538X
EAN: 2147483647
Year: 2001
Pages: 18
Authors: Ellie Quigley

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net