by Rick McMullin
IN THIS CHAPTER
In the preceding chapter, you saw the Bourne Again Shell (bash) in some
detail. Not everyone wants to use the bash shell, so several other shells
are included with most Linux systems. One of them is pdksh, a variation
on the Korn Shell.
In this chapter, we look at the pdksh shell and how it can be efficiently used. After reading this chapter, you will be familiar with the following topics:
We will also look at how you can customize your copy of pdksh, as well as several of the important commands and variables used by the shell.
The Korn shell, written by David Korn, was the third mainstream shell written for UNIX. Because of this, it incorporated many of the features of the Bourne and C shells (which were the first two shells). Because of the Korn shell's popularity among UNIX users, a version was developed for Linux called the Public Domain Korn Shell, or pdksh.
The current version of the Public Domain Korn Shell does not support all of the features that exist in the commercial version of the Korn shell. It does support most of the main features, however, and adds a few new features of its own.
Often, when you are entering commands at the command line, the complete text of the command is not necessary in order for pdksh to be able to determine what you want to do. Command-line completion enables you to type in a partial command, and then by entering a key sequence, tell pdksh to try to finish the command for you.
pdksh does not default to allowing the user to perform command-line completion. You must enter a command to tell pdksh that you want to be able to use command-line completion. In order to enable command-line completion, enter one of the following commands:
set -o emacs set -o vi
This causes pdksh to accept command editing that is similar to emacs or vi. Choose the editor that you are most familiar with, and use the basic editor commands for command-line editing.
After the command-line completion function has been enabled, you can perform command-line completion by pressing the Esc key twice (when using emacs command-line editing), or by pressing \ (when using vi command-line editing). For example, if your current directory contains the files
News/ bin/ games/ mail/ sample.text test/
and you want to edit the file sample.text using the vi text editor, you could enter the following command:
vi sample.text
After the s is typed, the only file that you could be referring to is sample.text, because it is the only file in the current directory that begins with the letter s. To get pdksh to finish the command when you are using emacs-style command editing, you must press the Esc key twice after you type the letter s:
vi s<escape><escape>
To get pdksh to finish the command when you are using vi command editing, you must press the \ key after you type the letter s:
vi s\
Either of these commands causes pdksh to finish the line for you and display the result on the screen. The command does not execute until you press the Enter key. This is done to give you a chance to confirm that the command pdksh came up with is the command that you really intended.
If the sample.text file is not the only file in the directory that begins with the letter s, pdksh completes the command as far as it can and then beeps, indicating that it needs more information to complete the command.
The pdksh shell makes typing commands easier by enabling the user to use wildcards. pdksh supports the same wildcards that bash does: * matches any character and any number of characters.
? matches any single character.
[...] matches any single character contained within the brackets.
The * wildcard can be used in a way that is similar to command-line completion. For example, if the current directory contains the files
News/ bin/ games/ mail/ sample.text test/
and you want to edit the sample.text file using the vi text editor, you can perform this task by using the following wildcard:
vi s*
The * matches any character (and any number of characters), so pdksh replaces s* with sample.text (the only file in the directory that matches the wildcard pattern).
This works reliably if there is only one file in the directory that starts with the letter s. If more than one file starts with the same letter, the shell tries to replace s* with the list of filenames that match the wildcard pattern, and runs vi on the first file in this list. After you quit editing the first file, the second file in the list is loaded into vi, and so on for each file that matched the wildcard pattern. If you intended to edit more than one file, this would be fine, but if you only wanted to edit the sample.text file, this command would not work the way you expected it to.
A more practical situation in which to use the * wildcard is when you want to execute the same command on multiple files that have similar filenames. For example, assume that the current directory contains the following files:
News/ bin/ games/ mail/ sample.text temp1.out temp2.out temp3.out test/
If you want to delete all of the files with an .out extension, you can do it by entering the following command:
rm *.out
In this case, pdksh replaces *.out with the names of all of the files in the directory that match the wildcard pattern. After pdksh performs this substitution, the following command is processed:
rm temp1.out temp2.out temp3.out
The rm command is invoked with the arguments of temp1.out, temp2.out, and temp3.out.
The ? wildcard functions in a similar way to the * wildcard, except that the ? wildcard matches only a single character. Assuming the same directory contents as in the previous example, the ? wildcard can be used to delete all of the files with the .out extension by entering the following command:
rm temp?.out
The [...] wildcard enables you to specify characters or ranges of characters to match. To print all of the files in the previous example that have the .doc extension, enter one of the following two commands:
rm temp[123].out rm temp[1-3].out
The pdksh shell supports a command history in much the same way as bash. The pdksh shell keeps track of the last HISTSIZE commands that have been entered (HISTSIZE is a user-definable pdksh variable).
pdksh stores the text of the last HISTSIZE commands in a history list. When you log in, the history list is initialized from a history file. The filename of the history file can be set using the HISTFILE pdksh variable. The default filename for the history file is .ksh_history. This file is located in your home directory. Notice that the file begins with a ., meaning that the file is hidden, and it appears in a directory listing only if you use the -a or -A option of the ls command.
The shell provides several ways of accessing the history list. The simplest way is to scroll through the commands that have been previously entered. In pdksh, this is done differently depending on whether you are using emacs or vi command editing.
If you are using emacs command editing, you scroll up through the history list by pressing Ctrl-p, and you scroll down through the list by pressing Ctrl-n. If you are using vi command-line editing, you scroll up through the history list by pressing either the k or - key, and you scroll down through the history list by pressing j or +.
The command that is on the command line can be edited. The pdksh shell supports a complex set of editing capabilities (most of which are beyond the scope of this book). You can use the left and right arrow keys to move along the command line. You can insert text at any point and delete text from the command line by using the backspace or delete keys. Most users should find these simple editing commands sufficient; for those who do not, there are many other more complicated ways of editing the command line.
Another method of using the history file is to display and edit it using fc (fix command), the built-in pdksh shell command. If you read Chapter 10, "bash," you may remember that bash supported another command called history, which allowed you to view and modify the history file. The history command was left out of the pdksh shell because all of its functionality could be provided by the fc command.
The fc command is used to edit the command history. It has a number of options, as is illustrated in the following command syntax:
fc [-e ename] [-nlr] [first] [last]
All options given in braces are optional. The -e portion of the command can be used to specify the text editor that is to be used for editing the commands in the command history. The first and last options are used to select a range of commands to take out of the history list. first and last can refer either to the number of a command in the history list, or to a string that fc tries to find in the history list.
The -n option is used to suppress command numbers when listing the history commands that matched the specified range. The -l command lists the matched commands to the screen. The -r option lists the matched commands in reverse order. In all cases except for the -l option, the matching commands are loaded into a text editor.
The text editor used by fc is found by taking the value of ename if the -e option was used. If this option was not used, fc uses the editor specified by the variable FCEDIT. If this variable does not exist, fc uses the value of the EDITOR variable. Finally, if none of these variables exists, the editor chosen is vi.
If you enter the fc command with no arguments, it loads the last command that was entered into the editor. Remember that when you exit the editor, fc attempts to execute any commands that are in the editor.
The easiest way to understand what the fc command does is to look at a few examples: fc loads the last command into the default editor.
fc -l lists the last 16 commands that were entered.
fc -l 5 10 lists the commands with history numbers between 5 and 10, inclusive.
fc 6 loads history command number 6 into the default editor.
fc mo loads into the default editor the most recent command that starts with the string mo.
Another way pdksh makes life easier for you is by supporting command aliases. Command aliases are commands that you can specify and execute. Alias commands are usually abbreviations of other commands.
You tell pdksh to execute a Linux command whenever it encounters the alias. For example, if you have a file in your directory that holds a list of things that you must do each day, and you typically edit the file every morning to update it, you might find yourself entering the following command on a regular basis:
vi things-to-do-today.txt
Because you are entering this command quite often, you might be inclined to create an alias for it to save yourself some typing. Instead of typing this command every time you want to edit the file, you can create an alias called ttd that causes the longer command to be executed.
To set up an alias such as this, you must use the pdksh alias command. To create the ttd alias, you enter the following command at the pdksh command prompt:
alias ttd='vi things-to-do-today.txt'
From the time that you enter the alias command until the time you exit from pdksh, the ttd command causes the longer command to be executed. If you decide after you enter an alias that you no longer want that alias, you can use the pdksh unalias command to delete the alias:
unalias ttd
After you use the unalias command to remove an alias, the alias no longer exists, and trying to execute it causes pdksh to report Command not found.
The following are some aliases that you might want to define: alias ll='ls -l'
alias log='logout'
alias ls='ls -F'
If you are a DOS user and you prefer to use DOS file commands, you may also want to define the following aliases: alias dir='ls'
alias copy='cp'
alias rename='mv'
alias md='mkdir'
alias rd='rmdir'
If you enter the alias command without any arguments, it prints all of the aliases that are already defined to the screen. There is a way to make sure that all of your alias commands get executed each time you start pdksh. This is done by using an initialization file, which we will discuss in the "Customizing Your pdksh" section, later in this chapter.
Input redirection is used to change the source of input for a command. Typically, when a command is entered in pdksh, the command expects some kind of input in order to do its job. Some of the simpler commands must get all of the information that they need passed to them on the command line. The rm command, for example, requires you to tell it on the command line which files you want to delete; if you do not specify any files it issues a prompt telling you to enter rm -h for help.
Other commands require more elaborate input than a simple directory name. The input for these commands is typically found in a file. For example, the wc (word count) command counts the number of characters, words, and lines in the input that was given to it. If you enter the wc command with a filename as an argument, wc returns the number of characters, words, and lines that are contained in that file. An example of this is
wc test 11 2 1
Another way to pass the contents of the test file to wc as input is to change (or redirect) the input of the wc command from the terminal to the test file. This results in the same output. The < character is used by pdksh to redirect the input to the current command from the file following the character. So, redirecting wc's input from the terminal to the test file is done by entering the following command:
wc < test
11 2 1
Input redirection is not used too often because most commands that require input from a file have an option to specify a filename on the command line. There are times, however, when you will come across a program that does not accept a filename as an input parameter, and yet the input that you want to give to the command exists in a file. Whenever this situation occurs, you can use input redirection to get around the problem.
Output redirection is more commonly used than input redirection. Output redirection enables you to redirect the output from a command into a file, as opposed to having the output displayed on the screen.
There are many situations in which this capability can be very useful. For example, if the output of a command is quite large and does not fit on the screen, you might want to redirect it to a file so you can later view it using a text editor. Output redirection is done in much the same way as input redirection. Instead of using the < symbol, the > symbol is used.
To redirect the output of an ls command into a file named directory.out, the following command is used:
ls > directory.out
Pipelines are a way to string together a series of commands. This means that the output from the first command in the pipeline is used as the input to the second command. You can tell pdksh to create a pipeline by typing two or more commands separated by the | character. The following is an example of using a pdksh pipeline:
cat test.file | sort | uniq
This is a fairly common pipeline. Here, the contents of test.file (the output from the cat command) are fed into the input of the sort command. The sort command, without any options, sorts its input alphabetically by the first field in the input. The sorted file is then piped into the uniq command. The uniq command removes any duplicate lines from the input. If test.file contains the lines
Sample dialog Hello there How are you today Hello there I am fine
the output from the pipeline is the following:
Hello there How are you today I am fine Sample dialog
All of the lines in the file have been sorted by the first word in the line, and one of the Hello there lines has been removed because of the uniq command.
pdksh has three levels of user prompts. The first level is what the user sees when the shell is waiting for a command to be typed. (This is what you normally see when you are working with the shell.) The default prompt is the $ character. If you do not like the dollar sign as the prompt or prefer to customize the prompt to your own requirements, you can do so by setting the value of the PS1 pdksh variable.
To set a variable, give the name and equal sign, and the string you want to set it to. Make sure you do not place any spaces on either side of the equal sign, or the shell will not interpret your command properly. For example, the line
PS1="! Tell me what to do"
sets the shell prompt to the string ! Tell me what to do. The pdksh shell keeps track of how many commands have been entered since it was started. This number is stored into the shell variable called !. When you include the ! in the prompt, it displays the current command number in the prompt. The previous prompt command causes the command number followed by the string Tell me what to do to be displayed on the command line each time pdksh is expecting you to enter a command.
The second level of prompt is displayed when pdksh is expecting more input from you in order to complete a command. The default for the second level prompt is >. If you want to change the second level prompt, you can do so by setting the value of the PS2 pdksh variable, as in the following example:
PS2=" I need more information"
This causes the string I need more information to be displayed on the command line whenever pdksh needs something from you to complete a command.
pdksh does not support the advanced prompt options that bash supports. There is not a predefined set of escape codes that you can put in a pdksh prompt variable to display such items as the time or current working directory. You can, however, put other pdksh variables into a prompt variable. For example, the following two prompts are valid:
PS1="(LOGNAME) "
PS1='($PW(D) `
The first example causes your prompt to be equal to your UNIX user name. The second example causes your prompt to be the current working directory. The single quotes are needed here so that the value of the PWD variable does not get assigned to the variable only the first time it is executed. If you use double quotes, the PWD variable is evaluated only when the command is first entered. (The prompt would always be the directory name of the directory that you are in when you enter the command.) The single quotes cause the value of the PS1 variable to be equal to the current value of the PWD variable. For more information on using these quotes, see Chapter 13, "Shell Programming."
Job control is the capability to control the execution behavior of a currently running process. Specifically, you can suspend a running process and cause it to resume running at a later time. The pdksh shell keeps track of all of the processes that it started, and you can suspend a running process or restart a suspended one at any time during the life of that process.
Pressing the Ctrl-Z key sequence suspends a running process. The bg command restarts a suspended process in the background, and the fg command restarts a process in the foreground.
These commands are most often used when a user wants to run a command in the background but accidentally starts it in the foreground. When a command is started in the foreground, it locks the shell from any further user interaction until the command completes execution. This is usually not a problem, because most commands only take a few seconds to execute. If the command you are running is going to take a long time, you typically start the command in the background so that you can continue to use pdksh to enter other commands while it completes running.
If you start a command in the foreground that is going to take a long time, your shell may be tied up for several minutes. If you have done this and want to continue executing the command in the background, enter the following:
control-z
bg
This suspends the command and restarts it in the background. The command continues to execute, and you have the control of pdksh.
One useful feature that pdksh supports, which is lacking in the Bourne Again Shell, is key bindings. This feature enables you to change the behavior of key combinations for the purpose of command-line editing.
If, for example, you do not like the fact that you have to use the emacs key sequence Ctrl-P to move up in the history buffer, you can change the key sequence for that command to something else. The syntax for doing the key binding is the following:
bind <key sequence> <command>
This feature effectively enables you to customize pdksh to have the exact feel that you want. One of the most commonly used key bindings is to bind the up, down, left, and right arrows to be used as they are in bash (for scrolling up and down the history list, and for moving left and right along the command line). This binding is typically found in your .kshrc file, which is the startup file for the shell (it is read whenever the shell starts).
The bind commands that are needed to create these bindings are as listed here:
bind `^[[`=prefix-2 bind `^XA'=up-history bind "^XB'=down-history bind `^XC'=forward-char bind `^XD'=backward-char
The following list gives some of the most useful editing commands that you can use for binding keys, along with the default binding and a description of each. You can get a listing of all of the editing commands that pdksh supports by typing the bind command without any arguments. abort (^G) is used to abort another editing command. It is most commonly used to stop a history list search.
backward-char (^B) moves the cursor backward one character. This command is often bound to the left arrow key.
backward-word (^[b) moves the cursor backward to the beginning of a word.
beginning-of-line (^A) moves the cursor to the beginning of the command line.
complete (^[^[) tells pdksh to try to complete the current command.
copy-last-arg (^[_) causes the last word of the previous command to be inserted at the cursor position.
delete-char-backward (ERASE) deletes the character that is to the left of the cursor.
delete-char-forward deletes the character to the right of the cursor.
delete-word-backward (^[ERASE) deletes the characters to the left of the cursor back to the first white space character that is encountered.
delete-word-forward (^[(d) deletes the characters to the right of the cursor up to the first character that occurs after a whitespace character.
down-history (^N) moves down one line in the history list. This command is often bound to the down arrow key.
end-of-line (^E) moves the cursor to the end of the current line.
forward-char (^F) moves the cursor forward one character. This command is often bound to the right arrow key.
forward-word (^[F) moves the cursor forward to the end of a word.
kill-line (KILL) deletes the current line.
kill-to-eol (^K) deletes all of the characters to the right of the cursor on the current line.
list (^[?) causes pdksh to list all of the possible command names or filenames that can complete the word in which the cursor is currently contained.
search-history (^R) searches the history list backward for the first command that contains the inputted characters.
transpose-chars (^T) exchanges the two characters on either side of the cursor. If the cursor is at the end of the command line it switches the last two characters on the line.
up-history (^P) moves up one command in the history list. This command is often bound to the up arrow key.
Many ways of customizing pdksh have been described in this chapter. Until now, though, the changes that you made only affected the current pdksh session. As soon as you quit pdksh, all of the customizations that you made are lost. There is a way of making the customizations more permanent.
This is done by storing all of your customizations in a pdksh initialization file. Users can put commands into this file that they want to be executed each and every time pdksh is started. Examples of commands that are typically found in this file are aliases and initializations of variables (such as the prompts).
In order to set up your customization file, you must tell pdksh where to look for the initialization file. This is different than with bash. The bash shell automatically knew where to look for its customization file. To tell pdksh where to look for the customization file, you must create a file in your home directory called .profile. This file is read and all of the commands in the file are executed each time you log into the system.
Here is a sample of the commands that you should place in your .profile files:
export ENV=$HOME/.kshrc EDITOR=emacs
The first line in the .profile file sets the ENV variable. This is the variable that pdksh looks at to find the initialization file that it should use. If you plan to customize pdksh, you should tell pdksh to look for a file in your home directory. The filename .kshrc is often used as the pdksh initialization filename, but you can pick another name if you want.
If you are not planning to customize pdksh, you can set the ENV variable to be equal to the system default pdksh initialization file. This file is in the /etc directory, and is called ksh.kshrc.
The second line in the .profile file sets the EDITOR variable. This is used by the .kshrc initialization file to determine what type of command-line editing commands to use for your session. If you prefer to use vi command-line editing, you can set this variable to be equal to vi.
Listing 11.1 shows most of what is contained in the system default ksh.kshrc file. If you want to add your own customizations to pdksh, you should copy this file into your home directory and then add the customizations that you want to your own copy of the file.
# NAME # ksh.kshrc - global initialization for ksh # # DESCRIPTION: # Each invocation of /bin/ksh processes the file pointed # to by $ENV (usually $HOME/.kshrc). # This file is intended as a global .kshrc file for the # Korn shell. A user's $HOME/.kshrc file simply requires # the line: # . /etc/ksh.kshrc # at or near the start to pick up the defaults in this # file which can then be overridden as desired. # # SEE ALSO: # $HOME/.kshrc # # RCSid: # $Id: ksh.kshrc,v 1.6 93/09/29 08:57:50 sjg Exp $ # # @(#)Copyright (c) 1991 Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise # use this file is hereby granted provided that # the above copyright notice and this notice are # left intact. case "$-" in *i*) # we are interactive # we may have su'ed so reset these # [BEG] NOTE: [END]SCO-UNIX doesn't have whoami, # install whoami.sh HOSTNAME=`hostname` USER=`whoami` PROMPT="<$USER@$HOSTNAME:!>$ " PPROMPT='<$USER@$HOSTNAME:$PWD:!>$ ` PS1=$PROMPT # $TTY is the tty we logged in on, # $tty is that which we are in now (might by pty) tty=`tty` tty=`basename $tty` set -o $EDITOR alias ls='ls -CF' alias h='fc -l | more' # the PD ksh is not 100% compatible case "$KSH_VERSION" in *PD*) # PD ksh case "$TERM" in xterm*) # bind arrow keys bind `^[[`=prefix-2 bind `^XA'=up-history bind `^XB'=down-history bind `^XC'=forward-char bind `^XD'=backward-char ;; esac ;; *) # real ksh ? ;; esac case "$TERM" in sun*) # these are not as neat as their csh equivalents if [ "$tty" != console ]; then # ilabel ILS='\033]L'; ILE='\033\\' # window title bar WLS='\033]l'; WLE='\033\\' fi ;; xterm*) ILS='\033]1;'; ILE='\007' WLS='\033]2;xterm: `; WLE='\007' ;; *) ;; esac # do we want window decorations? if [ "$ILS" ]; then ilabel () { print -n "${ILS}$*${ILE}"; } label () { print -n "${WLS}$*${WLE}"; } alias stripe='label "$USER@$HOST ($tty) - $PWD"' alias istripe='ilabel "$USER@$HOST ($tty)"' wftp () { ilabel "ftp $*"; "ftp" $*; eval istripe; } wcd () { "cd" $*; eval stripe; } wtelnet () { "telnet" "$@" eval istripe eval stripe } wsu () { "su" "$@" eval istripe eval stripe } alias su=wsu alias cd=wcd alias ftp=wftp alias telnet=wtelnet eval stripe eval istripe PS1=$PROMPT fi alias quit=exit alias cls=clear alias logout=exit alias bye=exit alias p='ps -l' alias j=jobs alias o='fg %-' # add your favourite aliases here ;; *) # non-interactive ;; esac
# commands for both interactive and non-interactive shells
Here are some of the most useful built-in pdksh commands: . reads and executes the contents of the file. (This will be discussed in more detail in Chapter 13.)
alias is used to set aliases, command nicknames that can be defined by the user.
bg (background command) forces a suspended process to continue to execute in the background.
cd (change working directory) changes the current working directory to the directory specified.
exit terminates the shell.
export causes the value of a variable to be made visible to all subprocesses that belong to the current shell.
fc (fix command) is used to edit the commands that are in the current history list.
fg (foreground command) forces a suspended process to continue to execute in the foreground.
kill is used to terminate another process.
pwd (print working directory) prints to the screen the directory in which the user is currently working.
unalias is used to remove aliases that have previously been defined using the alias command.
Some of the most useful pdksh variables are listed next, including the variable name, a short description, and default value (if one exists). EDITOR, FCEDIT The default editor for the fc bash command.
HISTFILE The name of the file that is used to store the command history.
HISTSIZE The size of the history list.
HOME The HOME directory of the current user.
OLDPWD The previous working directory (the one that was current before the current directory was entered).
PATH The search path that bash uses when looking for executable files.
PS1 The first level prompt that is displayed on the command line.
PS2 The second level prompt that is displayed when a command is expecting more input.
PWD The current working directory.
SECONDS The number of seconds that have elapsed since the current bash session was started.
We've looked at many of the features of the Public Domain Korn Shell (pdksh). It is similar to the Bourne Again Shell in many aspects, but it does add some new utilities.
In the next chapter, we look at tcsh, a version of the C shell that is available with Linux. After you have seen the features and the way you use the three shells, you should be able to decide which shell is best for you to use on a regular basis. Of course, you can use any shell at any time by simply typing its name.