Chapter 3. Customizing Your Environment

An environment is a collection of concepts that express the things a computer system or other set of tools does in terms designed to be understandable and coherent, and a look and feel that is comfortable. For example, your desk at work is an environment. Concepts involved in desk work usually include memos, phone calls, letters, forms, etc. The tools on or in your desk that you use to deal with these things include paper, staples, envelopes, pens, a telephone, a calculator, etc. Every one of these has a set of characteristics that express how you use it; such characteristics range from location on your desk or in a drawer (for simple tools) to more sophisticated things like which numbers the memory buttons on your phone are set to. Taken together, these characteristics make up your desk’s look and feel.

You customize the look and feel of your desk environment by putting pens where you can most easily reach them, programming your phone buttons, etc. In general, the more customization you have done, the more tailored to your personal needs—and therefore the more productive—your environment is.

Similarly, UNIX shells present you with such concepts as files, directories, and standard input and output, while UNIX itself gives you tools to work with these, such as file manipulation commands, text editors, and print queues. Your UNIX environment’s look and feel is determined by your keyboard and display, of course, but also by how you set up your directories, where you put each kind of file, and what names you give to files, directories, and commands. There are also more sophisticated ways of customizing your shell environment.

This chapter will look at the four most important features that bash provides for customizing your environment.

Special files

The files .bash_profile, .bash_logout, and .bashrc that are read by bash when you log in and out or start a new shell.

Aliases

Synonyms for commands or command strings that you can define for convenience.

Options

Controls for various aspects of your environment that you can turn on and off.

Variables

Changeable values that are referred to by a name. The shell and other programs can modify their behavior according to the values stored in the variables.

Although these features are not the only ones available, they form the basis for doing more advanced customization. They are also the features that are common to the various shells available on UNIX. Later chapters will cover more advanced shell features, such as the ability to program the shell.

The .bash_profile, .bash_logout, and .bashrc Files

Three files in your home directory have a special meaning to bash, providing a way for you to set up your account environment automatically when you log in and when you invoke another bash shell, and allowing you to perform commands when you log out. These files may already exist in your home directory, depending on how your system administrator has set up your account. If they don’t exist, your account is using only the default system file /etc/profile. You can easily create your own bash files using your favorite text editor. If you are unfamiliar with text editors available under UNIX, we suggest that you familiarize yourself with one of the better-known ones such as vi or emacs before proceeding further with the techniques described in this chapter.

The most important bash file, .bash_profile, is read and the commands in it executed by bash every time you log in to the system. If you examine your .bash_profile you will probably see lines similar to:

PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin
SHELL=/bin/bash
MANPATH=/usr/man:/usr/X11/man
EDITOR=/usr/bin/vi
     
PS1='h:w$ '
PS2='> '
export EDITOR

These lines define the basic environment for your login account. For the moment, it is probably best to leave these lines alone until you understand what they do. When editing your .bash_profile, just add your new lines after the existing ones.

Note that whatever you add to your .bash_profile won’t take effect until the file is re-read by logging out and then logging in again. Alternatively, you can also use the source command.[1] For example:

source .bash_profile

source executes the commands in the specified file, in this case .bash_profile, including any commands that you have added.

bash allows two synonyms for .bash_profile: .bash_login, derived from the C shell’s file named .login, and .profile, derived from the Bourne shell and Korn shell files named .profile. Only one of these three is read when you log in. If .bash_profile doesn’t exist in your home directory, then bash will look for .bash_login. If that doesn’t exist it will look for .profile.

One advantage of bash’s ability to look for either synonym is that you can retain your .profile if you have been using the Bourne shell. If you need to add bash-specific commands, you can put them in .bash_profile followed by the command source .profile. When you log in, all the bash-specific commands will be executed, and bash will source .profile, executing the remaining commands. If you decide to switch to using the Bourne shell you don’t have to modify your existing files. A similar approach was intended for .bash_login and the C shell .login, but due to differences in the basic syntax of the shells, this is not a good idea.

.bash_profile is read and executed only by the login shell. If you start up a new shell (a subshell) by typing bash on the command line, it will attempt to read commands from the file .bashrc. This scheme allows you the flexibility to separate startup commands needed at login time from those you might need when you run a subshell. If you need to have the same commands run regardless of whether it is a login shell or a subshell, you can just use the source command from within .bash_profile to execute .bashrc. If .bashrc doesn’t exist then no commands are executed when you start up a subshell.

The file .bash_logout is read and executed every time a login shell exits. It is provided to round out the capabilities for customizing your environment. If you wanted to execute some commands that remove temporary files from your account or record how much time you have spent logged in to the system then you would place the commands in .bash_logout. This file doesn’t have to exist in your account—if it isn’t there when you log out, then no extra commands are executed.

Aliases

If you have used UNIX for any length of time you will have noticed that there are many commands available and that some of them have cryptic names. Sometimes the commands you use the most have a string of options and arguments that need to be specified. Wouldn’t it be nice if there was a feature that let you rename the commands or allowed you to type in something simple instead of half a dozen options? Fortunately, bash provides such a feature: the alias.[2]

Aliases can be defined on the command line, in your .bash_profile, or in your .bashrc, using this form:

alias name=command

This syntax specifies that name is an alias for command. Whenever you type name as a command, bash will substitute command in its place when it executes the line. Notice that there are no spaces on either side of the equal sign (=); this is the required syntax.

There are a few basic ways to use an alias. The first, and simplest, is as a more mnemonic name for an existing command. Many commonly used UNIX commands have names that are poor mnemonics and are therefore excellent candidates for aliasing, the classic example being:

alias search=grep

grep, the UNIX file-searching utility, was named as an acronym for something like “Generalized Regular Expression Parser.”[3] This acronym may mean something to a computer scientist, but not to the office administrator who has to find Fred in a list of phone numbers. If you have to find Fred and you have the word search defined as an alias for grep, you can type:

$ search Fred phonelist

Some people who aren’t particularly good typists like to use aliases for typographical errors they make often. For example:

alias emcas=emacs
alias mali=mail
alias gerp=grep

This can be handy, but we feel you’re probably better off suffering with the error message and getting the correct spelling under your fingers. Another common way to use an alias is as a shorthand for a longer command string. For example, you may have a directory to which you need to go often. It’s buried deep in your directory hierarchy, so you want to set up an alias that will allow you to cd there without typing (or even remembering) the entire pathname:

alias cdvoy='cd sipp/demo/animation/voyager'

Notice the quotes around the full cd command; these are necessary if the string being aliased consists of more than one word.[4]

As another example, a useful option to the ls command is -F: it puts a slash (/) after directory files and an asterisk (*) after executable files. Since typing a dash followed by a capital letter is inconvenient, many people define an alias like this:

alias lf='ls -F'

A few things about aliases are important to remember. First, bash makes a textual substitution of the alias for that which it is aliasing; it may help to imagine bash passing your command through a text editor or word processor and issuing a “change” or “substitute” command before interpreting and executing it. Any special characters (such as wildcards like * and ?) that result when the alias is expanded are interpreted properly by the shell. [5] For example, to make it easier to print all of the files in your directory, you could define the alias:

alias printall='pr * | lpr'

Second, keep in mind that aliases are recursive, which means that it is possible to alias an alias. A legitimate objection to the previous example is that the alias, while mnemonic, is too long and doesn’t save enough typing. If we want to keep this alias but add a shorter abbreviation, we could define:

alias pa=printall

With recursive aliasing available it would seem possible to create an infinite loop:

alias ls='ls -l'

bash ensures that this loop cannot happen, because only the first word of the replacement text is checked for further aliasing; if that word is identical to the alias being expanded, it is not expanded a second time. The above command will work as expected (typing ls produces a long list with permissions, sizes, owners, etc.), while in more meaningless situations such as:

alias listfile=ls
alias ls=listfile

the alias listfile is ignored.

Aliases can be used only for the beginning of a command string—albeit with certain exceptions. In the cd example above, you might want to define an alias for the directory name alone, not for the entire command. But if you define:

alias anim=sipp/demo/animation/voyager

and then type cd anim, bash will probably print a message like anim: No such file or directory.

An obscure feature of bash’s alias facility—one not present in the analogous C shell feature—provides a way around this problem. If the value of an alias (the right side of the equal sign) ends in a blank, then bash tries to do alias substitution on the next word on the command line. To make the value of an alias end in a blank, you need to surround it with quotes.

Here is how you would use this capability to allow aliases for directory names, at least for use with the cd command. Just define:

alias cd='cd '

This causes bash to search for an alias for the directory name argument to cd, which in the previous example would enable it to expand the alias anim correctly.

Another way to define a directory variable for use with the cd command is to use the environment variable cdable_vars, discussed later in this chapter.

Finally, there are a few useful adjuncts to the basic alias command. If you type alias name without an equal sign (=) and value, the shell will print the alias’s value or alias name not found if it is undefined. If you type alias without any arguments, you get a list of all the aliases you have defined. The command unalias name removes any alias definition for its argument.

Aliases are very handy for creating a comfortable environment, but they have essentially been superseded by shell scripts and functions, which we will look at in the next chapter. These give you everything aliases do plus much more, so if you become proficient at them, you may find that you don’t need aliases anymore. However, aliases are ideal for novices who find UNIX to be a rather forbidding place, full of terseness and devoid of good mnemonics. Chapter 4 shows the order of precedence when, for example, an alias and a function have the same name.

Options

While aliases let you create convenient names for commands, they don’t really let you change the shell’s behavior. Options are one way of doing this. A shell option is a setting that is either “on” or “off.” While several options relate to arcane shell features that are of interest only to programmers, those that we will cover here are of interest to all users.

The basic commands that relate to options are set -o optionname and set +o optionname. You can change more than one option with the one set command by preceding each optionname with a -o or +o. The use of plus (+) and minus (-) signs is counterintuitive: the - turns the named option on, while the + turns it off. The reason for this incongruity is that the dash (-) is the conventional UNIX way of specifying options to a command, while the use of + is an afterthought.

Most options also have one-letter abbreviations that can be used in lieu of the set -o command; for example, set -o noglob can be abbreviated set -f. These abbreviations are carryovers from the Bourne shell. Like several other “extra” bash features, they exist to ensure upward compatibility; otherwise, their use is not encouraged.

Table 3-1 lists the options that are useful to general UNIX users. All of them are off by default except as noted.

Table 3-1. Basic shell options

Option

Description

emacs

Enters emacs editing mode (on by default)

ignoreeof

Doesn’t allow use of a single CTRL-D to log off; use the exit command to log off immediately (this has the same effect as setting the shell variable IGNOREEOF=10)

noclobber

Doesn’t allow output redirection (>) to overwrite an existing file

noglob

Doesn’t expand filename wildcards like * and ? (wildcard expansion is sometimes called globbing)

nounset

Indicates an error when trying to use a variable that is undefined

vi

Enters vi editing mode

There are several other options (21 in all; Appendix B lists them). To check the status of an option, just type set -o. bash will print a list of all options along with their settings.

shopt

bash 2.0 introduced a new built-in for configuring shell behaviour, shopt. This built-in is meant as a replacement for option configuration originally done through environment variables and the set command. [6]

The shopt -o functionality is a duplication of parts of the set command and is provided for completeness on the part of shopt, while retaining backward compatibility by its continued inclusion in set.

The format for this command is shopt options option-names. Table 3-2 lists shopt’s options.

Table 3-2. Options to shopt

Option

Meaning

-p

Displays a list of the settable options and their current values

-s

Sets each option name

-u

Unsets each option name

-q

Suppresses normal output; the return status indicates if a variable is set or unset

-o

Allows the values of the option names to be those defined for the -o option of the set command

The default action is to unset (turn off) the named options. If no options and arguments are given, or the -p option is used, shopt displays a list of the settable options and the values that they currently have. If -s or -u is also given, the list is confined to only those options that are set or unset, respectively.

A list of the most useful option names is given in Table 3-3. A complete list is given in Appendix B.

Table 3-3. shopt option names

Option

Meaning

cdable_vars

If set, an argument to the cd built-in command that is not a directory is assumed to be the name of a variable whose value is the directory to change to.

checkhash

If set, bash checks that a command found in the hash table exists before trying to execute it. If a hashed command no longer exists, a normal path search is performed.

cmdhist

If set, bash attempts to save all lines of a multiple-line command in the same history entry.

dotglob

If set, bash includes filenames beginning with a . (dot) in the results of pathname expansion.

execfail

If set, a non-interactive shell will not exit if it cannot execute the file specified as an argument to the exec command. An interactive shell does not exit if exec fails.

histappend

If set, the history list is appended to the file named by the value of the HISTFILE variable when the shell exits, rather than overwriting the file.

lithist

If set, and the cmdhist option is enabled, multiline commands are saved to the history with embedded newlines, rather than using semicolon separators where possible.

mailwarn

If set, and a file that bash is checking for mail has been accessed since the last time it was checked, the message “The mail in mailfile has been read” is displayed.

We’ll look at the use of the various options later in this chapter.

Shell Variables

There are several characteristics of your environment that you may want to customize but that cannot be expressed as an on/off choice. Characteristics of this type are specified in shell variables. Shell variables can specify everything from your prompt string to how often the shell checks for new mail.

Like an alias, a shell variable is a name that has a value associated with it. bash keeps track of several built-in shell variables; shell programmers can add their own. By convention, built-in variables should have names in all capital letters. bash does, however, have two exceptions.[7] The syntax for defining variables is somewhat similar to the syntax for aliases:

            varname=value

There must be no space on either side of the equal sign, and if the value is more than one word, it must be surrounded by quotes. To use the value of a variable in a command, precede its name by a dollar sign ($).

You can delete a variable with the command unset varname. Normally this isn’t useful, since all variables that don’t exist are assumed to be null, i.e., equal to the empty string “”. But if you use the set option nounset, which causes the shell to indicate an error when it encounters an undefined variable, then you may be interested in unset.

The easiest way to check a variable’s value is to use the echo built-in command. All echo does is print its arguments, but not until the shell has evaluated them. This includes—among other things that will be discussed later—taking the values of variables and expanding filename wildcards. So, if the variable wonderland has the value alice, typing:

$ echo "$wonderland"

will cause the shell to simply print alice. If the variable is undefined, the shell will print a blank line. A more verbose way to do this is:

$ echo "The value of $
            varname 
            is "$
            varname
            "."

The first dollar sign and the inner double quotes are backslash-escaped (i.e., preceded with so the shell doesn’t try to interpret them—see Chapter 1) so they appear literally in the output, which for the above example would be:

The value of $wonderland is "alice".

Variables and Quoting

Notice that we used double quotes around variables (and strings containing them) in these echo examples. In Chapter 1, we said that some special characters inside double quotes are still interpreted, while none are interpreted inside single quotes.

A special character that “survives” double quotes is the dollar sign—meaning that variables are evaluated. It’s possible to do without the double quotes in some cases; for example, we could have written the above echo command this way:

$ echo The value of $
               varname 
               is "$
               varname".

But double quotes are more generally correct. Here’s why. Suppose we did this:

$ fred='Four spaces between these words.'

Then if we entered the command echo $fred, the result would be:

Four spaces between these words.

What happened to the extra spaces? Without the double quotes, the shell splits the string into words after substituting the variable’s value, as it normally does when it processes command lines. The double quotes circumvent this part of the process (by making the shell think that the whole quoted string is a single word).

Therefore the command echo "$fred" prints this:

Four spaces between these    words.

The distinction between single and double quotes becomes particularly important when we start dealing with variables that contain user or file input later on.

Double quotes also allow other special characters to work, as we’ll see in Chapter 4, Chapter 6, and Chapter 7. But for now, we’ll revise the “When in doubt, use single quotes” rule in Chapter 1 by adding, “...unless a string contains a variable, in which case you should use double quotes.”

Built-In Variables

As with options, some built-in shell variables are meaningful to general UNIX users, while others are arcana for hackers. We’ll look at the more generally useful ones here, and we’ll save some of the more obscure ones for later chapters. Again, Appendix B contains a complete list.

Editing mode variables

Several shell variables relate to the command-line editing modes that we saw in the previous chapter. These are listed in Table 3-4.

Table 3-4. Editing mode variables

Variable

Meaning

HISTCMD

The history number of the current command.

HISTCONTROL

A list of patterns, separated by colons (:), which can have the following values. ignorespace: lines beginning with a space are not entered into the history list. ignoredups: lines matching the last history line are not entered. erasedups: all previous lines matching the current line are removed from the history list before the line is saved. ignoreboth: enables both ignorespace and ignoredups. [8]

HISTIGNORE

A list of patterns, separated by colons (:), used to decide which command lines to save in the history list. Patterns are considered to start at the beginning of the command line and must fully specify the line, i.e., no wildcard (*) is implicitly appended. The patterns are checked against the line after HISTCONTROL is applied. An ampersand (&) matches the previous line. An explicit & may be generated by escaping it with a backslash.[9]

HISTFILE

Name of history file in which the command history is saved. The default is ~/.bash_history.

HISTFILESIZE

The maximum number of lines to store in the history file. The default is 500. When this variable is assigned a value, the history file is truncated, if necessary, to the given number of lines.

HISTSIZE

The maximum number of commands to remember in the command history. The default is 500.

HISTTIMEFORMAT

If it is set and not null, its value is used as a format string for strftime(3) to print the time stamp associated with each history entry displayed by the history command. Time stamps are written to the history file so they may be preserved across shell sessions.[10]

FCEDIT

Pathname of the editor to use with the fc command.

[8] history_control is synonymous with HISTCONTROL in versions of bash prior to 2.0. Versions prior to 1.14 only define history_control. ignoreboth is not available in bash versions prior to 1.14. HISTCONTROL is a colon-separated list, and erasedups has been added in bash 3.0 and later.

[9] This variable is not available in versions of bash prior to 2.0.

[10] This variable is not available in versions of bash prior to 3.0.

In the previous chapter, we saw how bash numbers commands. To find out the current command number in an interactive shell, you can use the HISTCMD. Note that if you unset HISTCMD, it will lose its special meaning, even if you subsequently set it again.

We also saw in the last chapter how bash keeps the history list in memory and saves it to a file when you exit a shell session. The variables HISTFILESIZE and HISTSIZE allow you to set the maximum number of lines that the shell saves in the history file, and the maximum number of lines to “remember” in the history list, i.e., the lines that it displays with the history command.

Suppose you wanted to maintain a small history file in your home directory. By setting HISTFILESIZE to 100, you immediately cause the history file to allow a maximum of 100 lines. If it is already larger than the size you specify, it will be truncated.

HISTSIZE works in the same way, but only on the history that the current shell has in memory. When you exit an interactive shell, HISTSIZE will be the maximum number of lines saved in your history file. If you have already set HISTFILESIZE to be less than HISTSIZE, the saved list will be truncated.

You can also cut down on the size of your history file and history list by use of the HISTCONTROL variable. This is a colon-separated list of values. If it includes ignorespace, any commands that you type that start with a space won’t appear in the history. Even more useful is the ignoredups option. This discards consecutive entries from the history list that are duplicated. Suppose you want to monitor the size of a file with ls as it is being created. Normally, every time you type ls it will appear in your history. By setting HISTCONTROL to ignoredups, only the first ls will appear in the history.

The variable HISTIGNORE allows you to specify a list of patterns which the command line is checked against. If the command line matches one of the patterns, it is not entered into the history list. You can also request that it ignore duplicates by using the pattern &.

For example, suppose you didn’t want any command starting with l, nor any duplicates, to appear in the history. Setting HISTIGNORE to l*:& will do just that. Just as with other pattern matching we have seen, the wildcard after the l will match any command line starting with that letter.

Another useful variable is HISTTIMEFORMAT, which prepends a time stamp to each history entry showing when the command was executed. If it is unset or the value is null then no time stamp is written. If a format is given then time stamps are inserted using the specified format as part of the history and are shown with the history command.

The time stamp formats are shown in Table 3-5. Some of the results will be displayed using the particular format for the underlying locale, e.g., weekday names will be translated into the language being used on the system.

Table 3-5. Time stamp formats

Format

Replaced by

%a

The locale’s abbreviated weekday name

%A

The locale’s full weekday name

%b

The locale’s abbreviated month name

%B

The locale’s full month name

%c

The locale’s appropriate date and time representation

%C

The century number (the year divided by 100 and truncated to an integer) as a decimal number [00-99]

%d

The day of the month as a decimal number [01-31]

%D

The date in American format; the same value as %m/%d/%y.

%e

The day of the month as a decimal number [1-31]; a single digit is preceded by a space

%h

The same as %b

%H

The hour (24-hour clock) as a decimal number [00-23]

%I

The hour (12-hour clock) as a decimal number [01-12]

%j

The day of the year as a decimal number [001-366]

%m

The month as a decimal number [01-12]

%M

The minute as a decimal number [00-59]

%n

A newline character

%p

The locale’s equivalent of either a.m. or p.m

%r

The time in a.m. and p.m. notation; in the POSIX locale this is equivalent to %I:%M:%S %p

%R

The time in 24-hour notation (%H:%M)

%S

The second as a decimal number [00-61]

%t

A tab character

%T

The time (%H:%M:%S)

%u

The weekday as a decimal number [1-7], with 1 representing Monday

%U

The week number of the year (Sunday as the first day of the week) as a decimal number [00-53]

%V

The week number of the year (Monday as the first day of the week) as a decimal number [01-53]; if the week containing 1 January has four or more days in the new year, then it is considered week 1—otherwise, it is the last week of the previous year, and the next week is week 1

%w

The weekday as a decimal number [0-6], with 0 representing Sunday

%W

The week number of the year (Monday as the first day of the week) as a decimal number [00-53]; all days in a new year preceding the first Monday are considered to be in week 0

%x

The locale’s appropriate date representation

%X

The locale’s appropriate time representation

%y

The year without century as a decimal number [00-99]

%Y

The year with century as a decimal number

%Z

The timezone name or abbreviation, or by nothing if no timezone information exists

%%

%

If you wanted to have the date and time with each history entry, you could put:

HISTTIMEFORMAT="%y/%m/%d %T "

then the output of the history command would look something like:

...
78 04/11/26 17:14:05 HISTTIMEFORMAT="%y/%m/%d %T "
79 04/11/26 17:14:08 ls -l
80 04/11/26 17:14:09 history

If the history has never had a date format set before then all of the entries prior to setting the variable will get the time stamp of the time the variable was set. If you set HISTTIMEFORMAT to null and then set it to a format, the previous time stamps are retained and displayed in the new format.

Mail variables

Since the mail program is not running all the time, there is no way for it to inform you when you get new mail; therefore the shell does this instead.[11] The shell can’t actually check for incoming mail, but it can look at your mail file periodically and determine whether the file has been modified since the last check. The variables listed in Table 3-6 let you control how this works.

Table 3-6. Mail variables

Variable

Meaning

MAIL

Name of file to check for incoming mail

MAILCHECK

How often, in seconds, to check for new mail (default 60 seconds)

MAILPATH

List of filenames, separated by colons (:), to check for incoming mail

Under the simplest scenario, you use the standard UNIX mail program, and your mail file is /usr/mail/yourname or something similar. In this case, you would just set the variable MAIL to this filename if you want your mail checked:

MAIL=/usr/mail/yourname

If your system administrator hasn’t already done it for you, put a line like this in your .bash_profile.

However, some people use nonstandard mailers that use multiple mail files; MAILPATH was designed to accommodate this. bash will use the value of MAIL as the name of the file to check, unless MAILPATH is set; in which case, the shell will check each file in the MAILPATH list for new mail. You can use this mechanism to have the shell print a different message for each mail file: for each mail filename in MAILPATH, append a question mark followed by the message you want printed.

For example, let’s say you have a mail system that automatically sorts your mail into files according to the username of the sender. You have mail files called /usr/mail/you/martin, /usr/mail/you/geoffm, /usr/mail/you/paulr, etc. You define your MAILPATH as follows:

MAILPATH=/usr/mail/you/martin:/usr/mail/you/geoffm:
/usr/mail/you/paulr

If you get mail from Martin Lee, the file /usr/mail/you/martin will change. bash will notice the change within one minute and print the message:

You have new mail in /usr/mail/you/martin

If you are in the middle of running a command, the shell will wait until the command finishes (or is suspended) to print the message. To customize this further, you could define MAILPATH to be:

MAILPATH="
/usr/mail/you/martin?You have mail from Martin.:
/usr/mail/you/geoffm?Mail from Geoff has arrived.:
/usr/mail/you/paulr?There is new mail from Paul."

The backslashes at the end of each line allow you to continue your command on the next line. But be careful: you can’t indent subsequent lines. Now, if you get mail from Martin, the shell will print:

You have mail from Martin.

You can also use the variable $_ in the message to print the name of the current mail file. For example:

MAILPATH='/usr/mail/you?You have some new mail in $_'

When new mail arrives, this will print the line:

You have some new mail in /usr/mail/you

The ability to receive notification of mail can be switched on and off by using the mailwarn option to the shopt command.

Prompting variables

If you have seen enough experienced UNIX users at work, you may already have realized that the shell’s prompt is not engraved in stone. Many of these users have all kinds of things encoded in their prompts. It is possible to put useful information into the prompt, including the date and the current directory. We’ll give you some of the information you need to modify your own here; the rest will come in the next chapter.

Actually , bash uses four prompt strings. They are stored in the variables PS1, PS2, PS3, and PS4. The first of these is called the primary prompt string; it is your usual shell prompt, and its default value is "s-v$ “.[12] Many people like to set their primary prompt string to something containing their login name. Here is one way to do this:

PS1="u--> "

The u tells bash to insert the name of the current user into the prompt string. If your user name is alice, your prompt string will be "alice—>“. If you are a C shell user and, like many such people, are used to having a history number in your prompt string, bash can do this similarly to the C shell: if the sequence ! is used in the prompt string, it will substitute the history number. Thus, if you define your prompt string to be:

PS1="u !--> "

then your prompts will be like alice 1—>, alice 2—>, and so on.

But perhaps the most useful way to set up your prompt string is so that it always contains your current directory. This way, you needn’t type pwd to remember where you are. Here’s how:

PS1="w--> "

Table 3-7 lists the prompt customizations that are available.[13]

Table 3-7. Prompt string customizations

Command

Meaning

a

The ASCII bell character (007)

A

The current time in 24-hour HH:MM format

d

The date in “Weekday Month Day” format

D {format}

The format is passed to strftime(3) and the result is inserted into the prompt string; an empty format results in a locale-specific time representation; the braces are required

e

The ASCII escape character (033)

H

The hostname

h

The hostname up to the first “.”

j

The number of jobs currently managed by the shell

l

The basename of the shell’s terminal device name

A carriage return and line feed

A carriage return

s

The name of the shell

T

The current time in 12-hour HH:MM:SS format

The current time in HH:MM:SS format

@

The current time in 12-hour a.m./p.m. format

u

The username of the current user

v

The version of bash (e.g., 2.00)

V

The release of bash; the version and patchlevel (e.g., 2.00.0)

w

The current working directory

W

The basename of the current working directory

#

The command number of the current command

!

The history number of the current command

$

If the effective UID is 0, print a #, otherwise print a $

nn

Character code in octal

\

Print a backslash

[

Begin a sequence of non-printing characters, such as terminal control sequences

]

End a sequence of non-printing characters

PS2 is called the secondary prompt string; its default value is >. It is used when you type an incomplete line and hit RETURN, as an indication that you must finish your command. For example, assume that you start a quoted string but don’t close the quote. Then if you hit RETURN, the shell will print > and wait for you to finish the string:

$ echo "This is a long line, 
                  # PS1 for the command
> which is terminated down here" 
                  # PS2 for the continuation
                  $                                   
                  # PS1 for the next command

PS3 and PS4 relate to shell programming and debugging. They will be explained in Chapter 5, and Chapter 9.

Command search path

Another important variable is PATH, which helps the shell find the commands you enter.

As you probably know, every command you use is actually a file that contains code for your machine to run.[14] These files are called executable files or just executables for short. They are stored in various directories. Some directories, like /bin or /usr/bin, are standard on all UNIX systems; some depend on the particular version of UNIX you are using; some are unique to your machine; if you are a programmer, some may even be your own. In any case, there is no reason why you should have to know where a command’s executable file is in order to run it.

That is where PATH comes in. Its value is a list of directories that the shell searches every time you enter a command;[15] the directory names are separated by colons (:), just like the files in MAILPATH.

For example, if you type echo $PATH, you will see something like this:

/bin:/usr/bin:/usr/local/bin:/usr/X386/bin

Why should you care about your path? There are two main reasons. First, once you have read the later chapters of this book and you try writing your own shell programs, you will want to test them and eventually set aside a directory for them. Second, your system may be set up so that certain restricted commands’ executable files are kept in directories that are not listed in PATH. For example, there may be a directory /usr/games in which there are executables that are verboten during regular working hours.

Therefore you may want to add directories to your PATH. Let’s say you have created a bin directory under your login directory, which is /home/you, for your own shell scripts and programs. To add this directory to your PATH so that it is there every time you log in, put this line in your .bash_profile:

PATH=$PATH":/home/you/bin"

This line sets PATH to whatever it was before, followed immediately by a colon and /home/you/bin.

This is the safe way of doing it. When you enter a command, the shell searches directories in the order they appear in PATH until it finds an executable file. Therefore, if you have a shell script or program whose name is the same as an existing command, the shell will use the existing command—unless you type in the command’s full pathname to make it clear. For example, if you have created your own version of the more command in the above directory and your PATH is set up as in the last example, you will need to type /home/you/bin/more (or just ~/bin/more) to get your version.

The more reckless way of resetting your path is to put your own directory before the other directories:

PATH="/home/you/bin:"$PATH

This is unsafe because you are trusting that your own version of the more command works properly. But it is also risky for a more important reason: system security. If your PATH is set up in this way, you leave open a “hole” that is well known to computer crackers and mischief makers: they can install “Trojan horses” and do other things to steal files or do damage. (See Chapter 10 for more details.) Therefore, unless you have complete control of (and confidence in) everyone who uses your system, use the first of the two methods of adding your own command directory.

If you need to know which directory a command comes from, you need not look at directories in your PATH until you find it. The shell built-in command type prints the full pathname of the command you give it as argument, or just the command’s name and its type if it’s a built-in command itself (like cd), an alias, or a function (as we’ll see in Chapter 4).

Command hashing

You may be thinking that having to go and find a command in a large list of possible places would take a long time, and you’d be right. To speed things up, bash uses what is known as a hash table.

Every time the shell goes and finds a command in the search path, it enters it in the hash table. If you then use the command again, bash first checks the hash table to see if the command is listed. If it is, it uses the path given in the table and executes the command; otherwise, it just has to go and look for the command in the search path.

You can see what is currently in the hash table with the command hash:

$ hash
hits    command
   2    /bin/cat
   1    /usr/bin/stat
   2    /usr/bin/less
   1    /usr/bin/man
   2    /usr/bin/apropos
   2    /bin/more
   1    /bin/ln
   3    /bin/ls
   1    /bin/ps
   2    /bin/vi

This not only shows the hashed commands, but how many times they have been executed (the hits) during the current login session.

Supplying a command name to hash forces the shell to look up the command in the search path and enter it in the hash table. You can also make bash “forget” what is in the hash table by using hash -r to remove everything in the table or hash -d name to remove the specified name.[16] Another option, -p, allows you to enter a command into the hash table, even if the command doesn’t exist.[17]

Command hashing can be turned on and off with the hashall option to set. In general use, there shouldn’t be any need to turn it off.

Don’t be too concerned about the details of hashing. The command hashing and lookup is all done by bash without you knowing it’s taking place.

Directory search path and variables

CDPATH is a variable whose value, like that of PATH, is a list of directories separated by colons. Its purpose is to augment the functionality of the cd built-in command.

By default, CDPATH isn’t set (meaning that it is null), and when you type cd dirname, the shell will look in the current directory for a subdirectory that is called dirname.[18] If you set CDPATH, you give the shell a list of places to look for dirname; the list may or may not include the current directory.

Here is an example. Consider the alias for the long cd command from earlier in this chapter:

alias cdvoy='cd sipp/demo/animation/voyager'

Now suppose there were a few directories under this directory to which you need to go often; they are called src, bin, and doc. You define your CDPATH like this:

CDPATH=:~/sipp/demo/animation/voyager

In other words, you define your CDPATH to be the empty string (meaning the current directory) followed by ~/sipp/demo/animation/voyager.

With this setup, if you type cd doc, then the shell will look in the current directory for a (sub)directory called doc. Assuming that it doesn’t find one, it looks in the directory ~/sipp/demo/animation/voyager. The shell finds the doc directory there, so you go directly there.

If you often find yourself going to a specific group of directories as you work on a particular project, you can use CDPATH to get there quickly. Note that this feature will only be useful if you update it whenever your work habits change.

bash provides another shorthand mechanism for referring to directories; if you set the shell option cdable_vars using shopt,[19] any argument supplied to the cd command that is not a directory is assumed to be a variable.

We might define the variable anim to be ~/sipp/demo/animation/voyager. If we set cdable_vars and then type:

cd anim

the current directory will become ~/sipp/demo/animation/voyager.

Miscellaneous variables

We have covered the shell variables that are important from the standpoint of customization. There are also several that serve as status indicators and for various other miscellaneous purposes. Their meanings are relatively straightforward; the more basic ones are summarized in Table 3-8.

Table 3-8. Status variables

Variable

Meaning

HOME

Name of your home (login) directory

SECONDS

Number of seconds since the shell was invoked

BASH

Pathname of this instance of the shell you are running

BASH_VERSION

The version number of the shell you are running

BASH_VERSINFO

An array of version information for the shell you are running

PWD

Current directory

OLDPWD

Previous directory before the last cd command

The shell sets the values of these variables, except HOME (which is set by the login process: login, rshd, etc.). The first five are set at login time, the last two whenever you change directories. Although you can also set their values, just like any other variables, it is difficult to imagine any situation where you would want to. In the case of SECONDS, if you set it to a new value it will start counting from the value you give it, but if you unset SECONDS it will lose its special meaning, even if you subsequently set it again.

Customization and Subprocesses

Some of the variables discussed above are used by commands you may run—as opposed to the shell itself—so that they can determine certain aspects of your environment. The majority, however, are not even known outside the shell.

This dichotomy begs an important question: which shell “things” are known outside the shell, and which are only internal? This question is at the heart of many misunderstandings about the shell and shell programming. Before we answer, we’ll ask it again in a more precise way: which shell “things” are known to subprocesses? Remember that whenever you enter a command, you are telling the shell to run that command in a subprocess; furthermore, some complex programs may start their own subprocesses.

Now for the answer, which (like many UNIX concepts) is unfortunately not as simple as you might like. A few things are known to subprocesses, but the reverse is not true: subprocesses can never make these things known to the processes that created them.

Which things are known depends on whether the subprocess in question is a bash program (see Chapter 4) or an interactive shell. If the subprocess is a bash program, then it’s possible to propagate nearly every type of thing we’ve seen in this chapter—options and variables—plus a few we’ll see later.

Environment Variables

By default, only one kind of thing is known to all kinds of subprocesses: a special class of shell variables called environment variables. Some of the built-in variables we have seen are actually environment variables: HOME, MAIL, PATH, and PWD.

It should be clear why these and other variables need to be known by subprocesses. For example, text editors like vi and emacs need to know what kind of terminal you are using; the environment variable TERM is their way of determining this. As another example, most UNIX mail programs allow you to edit a message with your favorite text editor. How does mail know which editor to use? The value of EDITOR (or sometimes VISUAL).

Any variable can become an environment variable. First it must be defined as usual; then it must be exported with the command:[20]

export varnames

(varnames can be a list of variable names separated by blanks). You can combine variable assignment and the export into one statement:

export wonderland=alice

It is also possible to define variables to be in the environment of a particular subprocess (command) only, by preceding the command with the variable assignment, like this:

               varname=value command

You can put as many assignments before the command as you want.[21] For example, assume that you’re using the emacs editor. You are having problems getting it to work with your terminal, so you’re experimenting with different values of TERM. You can do this most easily by entering commands that look like:

TERM=trythisone 
               emacs 
               filename

emacs will have trythisone defined as its value of TERM, yet the environment variable in your shell will keep whatever value (if any) it had before. This syntax is surprisingly useful, but not very widely used; we won’t see it much throughout the remainder of this book.

Nevertheless, environment variables are important. Most .bash_profile files include definitions of environment variables; the sample built-in .bash_profile earlier in this chapter contained six such definitions:

PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin
SHELL=/bin/bash
MANPATH=/usr/man:/usr/X11/man
EDITOR=/usr/bin/vi
PS1='h:w$ '
PS2='> '
export EDITOR

You can find out which variables are environment variables and what their values are by typing export without arguments or by using the -p option to the command.

Some environment variable names have been used by so many applications that they have become standard across many shell environments. These variables are not built into bash, although some shells, such as the Korn shell, have them as built-ins. Table 3-9 lists the ones you are most likely to come across.

Table 3-9. Standard variables

Variable

Meaning

COLUMNS

The number of columns your display has[22]

EDITOR

Pathname of your text editor

LINES

The number of lines your display has

SHELL

Pathname of the shell you are running

TERM

The type of terminal that you are using

[22] Note that bash will set COLUMNS and LINES during certain situations, such as when the window the shell is in changes in size.

You may well find that some of these already exist in your own environment, most likely set from the system /etc/profile file (see Chapter 10). You can define them yourself in your .bash_profile and export them, as we did earlier.

Terminal types

The variable TERM is vitally important for any program that uses your entire screen or window, like a text editor. Such programs include all screen editors (such as vi and emacs), more, and countless third-party applications.

Because users are spending more and more time within programs, and less and less using the shell itself, it is extremely important that your TERM is set correctly. It’s really your system administrator’s job to help you do this (or to do it for you), but in case you need to do it yourself, here are a few guidelines.

The value of TERM must be a short character string with lowercase letters that appears as a filename in the terminfo database.[23] This database is a two-tiered directory of files under the root directory /usr/lib/terminfo. This directory contains subdirectories with single-character names; these in turn contain files of terminal information for all terminals whose names begin with that character. Each file describes how to tell the terminal in question to do certain common things like position the cursor on the screen, go into reverse video, scroll, insert text, and so on. The descriptions are in binary form (i.e., not readable by humans).

Names of terminal description files are the same as that of the terminal being described; sometimes an abbreviation is used. For example, the DEC VT100 has a description in the file /usr/lib/terminfo/v/vt100. An xterm terminal window under the X Window System has a description in /usr/lib/terminfo/x/xterm.

Sometimes your UNIX software will set up TERM incorrectly; this usually happens for X terminals and PC-based UNIX systems. Therefore, you should check the value of TERM by typing echo $TERM before going any further. If you find that your UNIX system isn’t setting the right value for you (especially likely if your terminal is of a different make from that of your computer), you need to find the appropriate value of TERM yourself.

The best way to find the TERM value—if you can’t find a local guru to do it for you—is to guess the terminfo name and search for a file of that name under /usr/lib/terminfo by using ls. For example, if your terminal is a Hewlett-Packard 70092, you could try:

$ cd /usr/lib/terminfo
$ ls 7/7*

If you are successful, you will see something like this:

70092   70092A  70092a

In this case, the three names are likely to be synonyms for (links to) the same terminal description, so you could use any one as a value of TERM. In other words, you could put any of these three lines in your .bash_profile:

TERM=70092
TERM=70092A
TERM=70092a

If you aren’t successful, ls will print an error message, and you will have to make another guess and try again. If you find that terminfo contains nothing that resembles your terminal, all is not lost. Consult your terminal’s manual to see if the terminal can emulate a more popular model; nowadays the odds for this are excellent.

Conversely, terminfo may have several entries that relate to your terminal, for submodels, special modes, etc. If you have a choice of which entry to use as your value of TERM, we suggest you test each one out with your text editor or any other screen-oriented programs you use and see which one works best.

The process is much simpler if you are using a windowing system, in which your “terminals” are logical portions of the screen rather than physical devices. In this case, operating system-dependent software was written to control your terminal window(s), so the odds are very good that if it knows how to handle window resizing and complex cursor motion, then it is capable of dealing with simple things like TERM. The X Window System, for example, automatically sets xterm as its value for TERM in an xterm terminal window.

Other common variables

Some programs, such as mail, need to know what type of editor you would like to use. In most cases they will default to a common editor like ed unless you set the EDITOR variable to the path of your favorite editor and export it in your .bash_profile.

Some programs run shells as subprocesses within themselves (e.g., many mail programs and the emacs editor’s shell mode); by convention they use the SHELL variable to determine which shell to use. SHELL is usually set by the process that invokes the login shell; usually login or something like rshd if you are logged in remotely. bash sets it only if it hasn’t already been set.

You may have noticed that the value of SHELL looks the same as BASH. These two variables serve slightly different purposes. BASH is set to the pathname of the current shell, whether it is an interactive shell or not. SHELL, on the other hand, is set to the name of your login shell, which may be a completely different shell.

COLUMNS and LINES are used by screen-oriented editors like vi. In most cases a default is used if they are undefined, but if you are having display problems with screen-oriented applications then you should check these variables to see if they are correct.

The Environment File

Although environment variables will always be known to subprocesses, the shell must be explicitly told which other variables, options, aliases, and so on, are to be communicated to subprocesses. The way to do this is to put all such definitions into the environment file. bash’s default environment file is the .bashrc file that we touched on briefly at the beginning of this chapter.

Remember, if you take your definitions out of .bash_profile and put them in .bashrc, you will have to have the line source .bashrc at the end of your .bash_profile so that the definitions become available to the login shell.

The idea of the environment file comes from the C shell’s .cshrc file. This is reflected in the choice of the name .bashrc. The rc suffix for initialization files is practically universal throughout the UNIX world.[24]

As a general rule, you should put as few definitions as possible in .bash_profile and as many as possible in your environment file. Because definitions add to rather than take away from an environment, there is little chance that they will cause something in a subprocess not to work properly. (An exception might be name clashes if you go overboard with aliases.)

The only things that really need to be in .bash_profile are environment variables and their exports and commands that aren’t definitions but actually run or produce output when you log in. Option and alias definitions should go into the environment file. In fact, there are many bash users who have tiny .bash_profile files, e.g.:

stty stop ^S intr ^C erase ^? 
date
source .bashrc

Although this is a small .bash_profile, this user’s environment file could be huge.

Customization Hints

You should feel free to try any of the techniques presented in this chapter. The best strategy is to test something out by typing it into the shell during your login session; then if you decide you want to make it a permanent part of your environment, add it to your .bash_profile.

A nice, painless way to add to your .bash_profile without going into a text editor makes use of the echo command and one of bash’s editing modes. If you type a customization command in and later decide to add it to your .bash_profile, you can recall it via CTRL-P or CTRL-R (in emacs-mode) or j, -, or ? (vi-mode). Let’s say the line is:

PS1="u !--> "

After you recall it, edit the line so that it is preceded by an echo command, surrounded by single quotes, and followed by an I/O redirector that (as you will see in Chapter 7) appends the output to ~/.bash_profile:

$ echo 'PS1="u !--> " ' >> ~/.bash_profile

Remember that the single quotes are important because they prevent the shell from trying to interpret things like dollar signs, double quotes, and exclamation points. Also make sure that you use a double right-caret (>>). A single one will overwrite the file rather than appending to it.



[1] You can also use the synonymous command dot (.).

[2] C shell users should note that the bash alias feature does not support arguments in alias expansions, as C shell aliases do. This functionality is provided by functions, which we’ll look at in Chapter 4.

[3] Another theory has it that grep stands for the command “g/re/p”, in the old ed text editor, which does essentially the same thing as grep.

[4] This contrasts with C shell aliases, in which the quotes aren’t required.

[5] An important corollary: wildcards and other special characters cannot be used in the names of aliases, i.e., on the left side of the equal sign.

[6] Appendix B provides a complete list of shopt shell options and the corresponding environment variables in earlier versions of the shell.

[7] Versions prior to 2.0 have many more lowercase built-in variables. Most of these are now obsolete, the functionality having been moved to the shopt command.

[11] BSD UNIX users should note that the biff command on those systems does a better job of informing you about new mail; while bash only prints “you have new mail” messages right before it prints command prompts, biff can do so at any time.

[12] In versions of bash prior to 2.0, the default is "bash$ “.

[13] a, e, H, T, @, v, and V are not available in versions prior to 2.0. D was introduced in bash 2.05b.

[14] Unless it’s a built-in command (one of those shown in boldface, like cd and echo), in which case the code is simply part of the executable file for the entire shell.

[15] Unless the command name contains a slash (/), in which case the search does not take place.

[16] The -d option is not available in versions of bash prior to 2.05b.

[17] The -p option is not available in versions of bash prior to 2.0.

[18] This search is disabled when dirname starts with a slash. It is also disabled when dirname starts with ./ or ../.

[19] In versions of bash prior to 2.0, cdable_vars is a shell variable that you can set and unset.

[20] Unless automatic exporting has been turned on by set -a or set -o allexport, in which case all variables that are assigned to will be exported.

[21] There is an obscure option, set -k, that lets you put this type of environment variable definition anywhere on the command line, not just at the beginning.

[23] Note that most modern UNIX systems now use a database rather than a flat file for the terminal descriptions.

[24] According to the folklore, it stands for “run commands” and has its origins in old DEC operating systems.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.224.44.108