Полезная информация

TOC BACK FORWARD HOME

UNIX Unleashed, System Administrator's Edition

- 10 -

The Bourne Again Shell

by Sriranga Veeraraghavan

Bash stands for GNU Bourne Again Shell and is the GNU project's shell. It will be the default shell on the GNU operating system. As its name implies, Bash is based on the Bourne shell, sh, originally written by Stephen Bourne.

Bash includes c shell (csh), tc shell (tcsh) and Korn shell (ksh) features while only differing slightly from the Bourne shell, sh. Thus most sh scripts can be run in bash with no modification. The differences between bash and sh are because bash is an implementation of the IEEE POSIX 1003.2/ISO 9945.2 Shell and Tools specification.

Bash was written by Brian Fox (bfox@gnu.ai.mit.edu) of the Free Software Foundation and is currently maintained by Chet Ramey (chet@ins.cwru.edu) of Case Western Reserve University.

Bash is distributed free, which means that anyone is free to use it and redistribute it under certain conditions, but it is not public domain. It is distributed under the GNU General Public License, a copy of which comes with the distribution. As stated in the license, bash does not come with a warranty, so the author and maintainer should not be considered a source of technical support.

The most recent release version of bash as of this writing is 2.0.0, which was released on December 31, 1996, but version 2.0.1 is currently being beta tested. The next release version of bash will, according to the FAQ, "appear sometime in 1997. Never make predictions." The predecessor to version 2.0.x was version 1.14.7, which was released on August 28, 1996.

This chapter concentrates on version 2.0.0 because it implements several new features, including one-dimensional arrays and the shopt built-in. Unless noted, all examples will also run under version 1.14.7.

Bash is available for anonymous ftp from any of the GNU archive sites, including the main GNU archive site, ftp://prep.ai.mit.edu/pub/gnu/bash-2.0.tar.gz. It is also available from the maintainer's machine, ftp://slc2.ins.cwru.edu/pub/dist/bash-2.0.tar.gz.

Bash is extremely portable and can be built on most UNIX systems because many of the environment dependent variables are determined at build time. Bash has been ported as a shell for several non-unix platforms like QNX, Minix, OS/2, Windows 95 and Windows NT. It is the default shell for BeOS.

The bash FAQ is available at ftp://slc2.ins.cwru.edu/pub/bash/FAQ.

Additional bash help can be obtained from Usenet Newsgroups such as gnu.bash.bug and comp.unix.shell.

Features

Bash incorporates several features that make it an excellent shell for novices and experts alike. Many of the features are similar to csh, tcsh and ksh, which eases the difficulty encountered in switching between them.

Definitions

The following terms are defined in the BASH(1) manual page and are used in the same context in this chapter:
blank A space or tab.
word A sequence of characters considered as a single unit by the shell. Also known as a token.
name A word consisting only of alphanumeric characters and underscores, and beginning with an alphabetic character or an underscore. Also referred to as an identifier.
metacharacter A character that, when unquoted, separates words. One of the following: | & ; ( ) < > space tab
control operator A token that performs a control function. It is one of the following symbols: | & && ; ;; ( ) | <newline>

Installing Bash

Bash is available on many systems, but if you must install bash, the following section covers the procedure.

Requirements

The following applications are required to build/install bash:

To make sure these programs are available, use the whereis command.

Procedure

Once the file bash-2.0.tar.gz has been obtained, use the following procedure to build bash:

1. cd into the directory containing the g-zipped tar file, bash-2.0.tar.gz.

2. Unzip the file:

gunzip bash-2.0.tar.gz


			
The file bash-2.0.tar.gz will be replaced by the file bash-2.0.tar. To see what gunzip does, give it the -v option.

3. Extract the source files from bash-2.0.tar:

tar -xvf bash-2.0.tar


			
This creates the directory bash-2.0, and puts the source files in that directory.

4. cd into the directory with the source files:

cd bash-2.0


			
5. Configure the source tree for your machine:

./configure


			
The shell script configure attempts to guess correct values for system-dependent variables used during compilation and then creates appropriate makefiles along with the following files:

config.h, the file containing system-dependent definitions

config.status, a shell script that recreates the current configuration

config.cache, the file that saves the test results to speed up reconfiguring

config.log, the file containing the compiler output

The configuration can be customized, but the default configuration is usually good enough for most installs. Configure has a help option that details the other configuration options:

./configure --help


			
One useful configuration option specifies which directory bash should be installed in instead of the default location, /usr/local:

./configure --prefix=[directory]


			
On some older versions of System V, if configure is executed from csh, csh may try and execute configure as a csh script. To avoid this use:

sh ./configure


			
The configuration process can take some time, about 15 minutes on a Sun Sparc10 and about 10 minutes on a Sun Ultra1.

6. Build bash:

make


			
By default, make will use gcc, even if cc is installed and works. To use cc:

make CC="cc"


			
The make also takes a long time, about 20 to 25 minutes on a Sun Sparc10 and 15 to 20 minutes on a Sun Ultra1.

7. Test the build (optional but recommended):

make tests


			
This will make and run the tests that come with the source code to confirm that bash works properly. If any problems are detected, bash should probably be rebuilt. After the tests are completed, it is probably a good idea to invoke the newly built version and execute some commands to make confirm that it works.

8. Strip the executable to remove debugging symbols (optional but recommended):


			
strip bash

			
To see the effect of strip on the size of the bash executable:

ls -l bash ; strip bash ; ls -l bash

This reduces the size of the bash executable by about 1 MB on a Sun Sparc. The actual size reduction varies, depending on the compiler used, the options supplied to the compiler, the hardware platform, and so on.

9. Install bash and its documentation:

make install

This places bash in the default location, /usr/local. To see what the default install will do without actually installing bash:

make -n install

To install bash in another location other than the configured one:

make install prefix=[directory]


			

Invocation

Bash can be invoked in several different modes. The two major modes are interactive and non-interactive.

Interactive Shells

An interactive shell is a shell whose standard input and standard output are connected to a terminal. The three major types of interactive shells are login, non-login, and restricted.

Bash determines whether a shell is interactive by using the isatty library call. In initialization files and scripts, interactive shells can be determined by using tty -s or test -t, which return 0 (true) if the standard input is a tty. Interactive shells set $PS1, so testing for its existence is another common method of identifying interactive shells.

When bash is invoked as a login shell, bash attempts to read several initialization files and reports any errors it encounters. If initialization files do not exist, then a default environment is used. First, bash reads and executes commands in /etc/profile. Then it reads and executes commands from the first one of the files ~/.bash_profile, ~/.bash_login and ~/.profile that exists and is readable. Bash can be given the --noprofile option to prevent it from reading any initialization files. When a login shell exits, if ~/.bash_logout exists, bash will read and execute commands from it. Only login shells will read the ~/.bash_logout when exiting.

When a non-login interactive bash is started, bash reads and executes commands from ~/.bashrc file. A non-login interactive bash can be forced to read a different initialization file by using the --rcfile file option. It can be prevented from reading any initialization files by giving the --norc option.

Bash can also be started in posix mode if it is given the --posix option. In posix mode, bash first checks to see if $ENV is defined. If it is, bash expands $ENV to a filename and then reads and executes commands from that file. Bash will not read any other startup files when invoked in posix mode. Under posix mode, the behavior of bash changes slightly to conform with the POSIX standard.

On some systems where bash is named sh (for instance, Linux). In such instances bash will startup like sh and then enter posix mode. A login bash invoked as sh, will try to read and execute commands in /etc/profile followed by ~/.profile. The --noprofile option will prevent the reading of these initialization files. If a non-login bash invoked as sh, its startup behavior is the same as in posix mode.

The final interactive mode of interest is restricted mode. Bash can be started in restricted mode by using the -r option or invoking it as rbash. In restricted mode, bash behaves normally, except that it does not allow certain operations, such as changing directories, modifying $SHELL or $PATH, running exec, running commands containing /, and using redirection. The restricted mode is not available in bash 1.x.

Non-Interactive Shells

Non-interactive bash shells are mostly used to run shell scripts. A non-interactive shell also runs when bash is invoked with the -c option. When a non-interactive bash is started, bash looks at $BASH_ENV. If it exists, bash tries to read and execute commands from the file named by the variable. The behavior is as if the command:

if [ -n "$BASH_ENV" ] ; then . "$BASH_ENV"; fi

was executed. The filename that $BASH_ENV specifies is not searched against $PATH.

Invocation Options

As noted above, bash can be invoked with several single character and multicharacter options. Following is a summary of these options along with a brief description of the function they serve:
-c string This option causes commands to be read from string. If string contains more than one word, the first word is assigned to $0, the command name, and the other words are assigned to the positional parameters, that is, $1, $2, and so on. An example would be: bash -c ls /tmp
-i This option forces bash to run in interactive mode.
-r
--restricted These options force bash to run in restricted mode. These options are not available in bash 1.x.
--login This option forces bash to behave as if it were the login shell.
--posix This option forces bash to conform to the POSIX standard. This option is implied if bash is invoked with the name sh.
--verbose This option forces bash to echo all input lines after they are read.
--help This option causes bash to print a usage message.
--version This option causes bash to print out version information. In version 2.0 or newer, bash will exit successfully after printing out the version information. In versions 1.14.x and earlier, bash must be exited manually.
--noprofile This option causes an interactive login bash to skip reading initialization files.
--norc This option causes an interactive non-login bash to skip reading the ~/.bashrc
--rcfile file This option causes an interactive non-login bash to read initialization commands from file instead of ~/.bashrc

Using Bash as the Login Shell

The most common way to change the login shell is to use the change shell command, chsh. On some systems where chsh is not available, passwd -s or passwd -e can be used to change the shell. On still other systems, ypchsh must be used to change the shell. On many systems (for instance, SunOS 5.5.x), the full pathname to the bash executable has to be included in /etc/shells before bash can be specified as a valid login shell.

If the default login shell cannot be changed, bash can still run as the login shell by modifying the initialization files for the default shell. If the default login shell is csh or tcsh and bash is in /usr/local/bin/bash, adding the following line to ~/.login will allow bash to be executed as the login shell:

if ( -f /usr/local/bin/bash ) exec /usr/local/bin/bash --login

It is better to invoke bash in the ~/.login file because it is read only at login time, unlike the ~/.cshrc file which is read every time a csh is started. Executing bash in the ~/.cshrc could lead to problems when csh scripts try to run. The best way to invoke bash in the ~/.cshrc file is to run bash only if the csh is interactive:

if ( $?prompt ) exec /usr/local/bin/bash --login

If the login shell is sh or ksh, two things must be done. First, in ~/.profile, a line similar to the following must be added:

[ -f /usr/local/bin/bash ] && exec /usr/local/bin/bash --login

Second, an empty file, ~/.bash_profile, must be created to prevent the executed bash from attempting to read ~/.profile, and re-executing itself. Alternatively the following could be added to ~/.profile:

case "$0" in
    *bash)
        : "Bash already running"
        ;;
    *)
        [ -f /usr/local/bin/bash ] && exec /usr/local/bin/bash --login
        ;;
esac

If you use a case statement in the ~/.profile, an empty ~/.bash_profile does not need to be created.

Syntax

Bash supports a syntax similar to sh and ksh.

Variables

Bash 2.0 supports both scalar and array variables. To set variables in bash, a standard sh assignment statement is used:

name=value

Array variables can be set in two ways. The first form sets a single element:

name[index]=value

The second form can be used to set multiple elements (not the entire array):

name=(value1 ... valuen)

In this form, consecutive array indices beginning at 0 are used. For example

myarray=(derri terry mike gene)

is equivalent to

myarray[0]=derri
myarray[1]=terry
myarray[2]=mike
myarray[3]=gene

When setting multiple array elements, an array index can be placed before the value, so a third way to do the preceding assignments is:

myarray=([0]=derri [3]=gene [2]=mike [1]=terry)

There is no maximum limit on array indices nor is there any requirement that all consecutive indices be used.

Bash also supports two built-in commands, declare and typeset, that allow the modification of variable attributes. These two commands are synonymous and allow for variables to be designated as arrays, integers, and read only along with marking variables for export. The declare command should be used since the typeset command is now obsolete.

A variable marked as an integer behaves similarly to the int type in the C programming language when it is used in an arithmetic expression. Read only variables, once assigned, cannot be reassigned. The commands declare and typeset accept the following options:

-a Sets/unsets the array attribute for a variable
declare -a foo # foo is now an array
[-/+] i Sets/unsets the integer attribute for a variable
declare -i foo # foo will be treated as an integer
declare +i bar # bar will be treated normally
[-/+] r Sets the read only attribute for a variable.
declare -r foo # foo's value cannot be changed
declare +r foo # generates an error, if foo is readonly
[-/+] x Sets/unsets the export attribute for a variable
declare -x foo # foo is marked for export
declare +r bar # bar will not be exported
-p variable Shows a variables attributes. If a variable is not given, displays all variables and their values. This option is not available in bash 1.x.

Exporting variables for use in the environment is the same as in sh. For cshand tcsh users, it is similar to creating environment variables with setenv. Marking a variable for export means that it is available for use in the environment of a shell's child processes. Exporting can be done in two ways:

export name
export name=value

The first form marks the named variable for export. The second form assigns the given value to the named variable and then marks that variable for export. More than one name or name=value pair may be given. Variables can be unexported if the -n option is given. If export is invoked by itself or if it is invoked with a -p option, it will print a list of the currently exported variables. Both of the following examples are valid methods of exporting the variable PATH:

PATH=/bin:/sbin:/usr/bin:/usr/local/bin:/usr/ucb
export PATH
export PATH=/bin:/sbin:/usr/bin:/usr/local/bin:/usr/ucb

In addition to these attributes, bash allows for variables to be marked as local, using the local built-in command. Local means that a separate instance of the variable is created for the function in which local is used (local can only be used in functions). There are two forms:

local name
local name=value

The first form creates a variable of the given name and marks it as local. The second creates a variable of the given name with the given value . If local is invoked without name or name=value, it will print out a list of the current local variables.

Bash automatically sets several variables at startup time, and several of these have special properties as long as they are not unset. The following is a partial list of these shell variables:
$PWD The current working directory as set by the cd command.
$UID Expands to the numeric user ID of the current user, initialized at shell startup.
$BASH Expands to the full pathname used to invoke this instance of bash.
$BASH_VERSION Expands to a string describing the version of this instance of bash.
$BASH_VERSINFO An array variable whose members hold version information for this instance of bash.
$SHLVL Incremented by one each time an instance of bash is started. This variable is useful for determining if the built-in exit command will end the current session.
$REPLY Expands to the last input line read by the read built-in command when it is given no arguments.
$RANDOM This parameter generates a random integer between 0 and 32767 each time it is referenced. The sequence of random numbers can be initialized by assigning a value to $RANDOM. If $RANDOM is unset, it loses its special properties, even if it is subsequently reset.
$SECONDS Each time this parameter is referenced, the number of seconds since shell invocation is returned. If a value is assigned to $SECONDS, the value returned upon subsequent references is the number of seconds since the assignment plus the value assigned. If $SECONDS is unset, it loses its special properties, even if it is subsequently reset.
$HISTCMD The history number, or index in the history list, of the current command. If $HISTCMD is unset, it loses its special properties, even if it is subsequently reset.
$IFS The Internal Field Separator that is used by the parser for word splitting after expansion. $IFS is also used to split lines into words with the read built-in command. The default value is <space><tab><newline>

The following variables are used by bash, but are initially set by other programs:
$PATH

The search path for commands. It is a colon-separated list of directories in which the shell looks for commands. A common value is

PATH=/bin:/sbin:/usr/bin:/usr/local/bin:/usr/ucb
$HOME The home directory of the current user; the default argument for the cd built-in command.

In addition to these types of variables, bash also supports two additional types of parameters: positional and special. Both of these parameters have their values assigned by bash, but only the positional parameter's values can be changed.

Positional parameters are denoted by one or more digits starting at 1, and can be accessed individually by giving the argument number as the variable name. If the argument number consists of multiple digits, brackets must be used. For example the first argument can be accessed as $1 but the eleventh argument must be accessed as ${11}.

There are several special parameters associated with positional parameters:
$* This expands to a single word containing the list of all positional parameters separated by the first character of $IFS (normally a space).
$@ This positional parameter is replaced with a series of words rather than a single word as with $*.
$# This expands to the number of positional parameters.

The positional parameters are the arguments that a function or shell script is invoked with. This means that the value of $1 is different inside and outside a function.

The other special parameters relate to command execution and are covered in the "History Variables" section.

Both variables and arrays can be unset with the unset command:

unset name
unset -v name

The -v option in the second form is used to indicate to unset that name is a shell variable. It is not required.

Expansion

When bash encounters an assignment statement of the form:

name=value

the value is assigned to name after tilde, variable, and string expansions are performed. Bash will also do command substitution, arithmetic evaluation and quote removal on the value before it is assigned.

Tilde expansion is performed when a ~ (tilde) is the first character of a word. When appropriate, bash treats the characters following the tilde as a login name and tries to substitute the home directory associated with characters after the tilde. If the tilde appears by itself or is followed by a slash (/) and $HOME is set, bash replaces the tilde with the value of $HOME. Otherwise, it replaces the tilde with the home directory of the current user. If ~+ is given, bash replaces it with the value of $PWD, the current directory. If ~- is given, it is replaced with the value of $OLDPWD, the previous working directory. If none of the tilde expansions work, bash leaves the tilde as is.

Bash performs variable expansion when a $ is encountered. The simplest forms are:

foo=$bar
foo=${bar}

Here the value of the variable $bar is substituted and assigned to the variable $foo. In the second form, the brackets are present to explicitly indicate to the parser where the variable's name ends. For example:

foo=24
echo $footh

prints the value of the variable $footh, whereas

foo=24
echo ${foo}th

prints 24th, which is the value of foo with a "th" appended.:

Variables can also be substituted in several more advanced ways:

${bar:?"Error, no bar"}


			
			
${bar:="foose"}

			
			
foo=${bar:-"foose"}

			
			
foo=${bar:+"foose"}

			

Bash also supports substring variable expansions, which can be used to assign parts of one variable value to another:

foo=${bar:offset:length}

The offset is the starting position in $bar, and length is the number of characters to get. If the length is not specified, all characters after the offset are returned.

Command substitution is performed when a command is given as:

$(command)
´´command´´

Both forms are replaced with the output of the command. Unless given in a double quoted context, each output line from the command becomes a separate word. In a double quoted context, the output is treated as a single word with embedded newlines.

Arithmetic evaluation is performed when the following form is encountered:

$((expression))

The expression will be evaluated according to the C programming language rules, and the result will be substituted. For example:

foo=$(( ((5 + 3*2) - 4) / 2 ))

will set the value of foo to 3 (not 3.5, because this is integer arithmetic, or 6, due to operator precedence).

Quote removal is performed after all the other expansions have taken place, and it removes all the remaining unquoted occurrences of the characters: \ (backslash), ' (single quote) and " (double quote).

Bash performs two other types of expansion. These are brace and pathname expansions. These are primarily used for commands that are issued on the command line. Brace expansion is primarily used to create several strings that have common elements. The general form is:

stringx{string1, string2, ... , stringn}stringy

Bash expands this into[]strings, each containing one of the strings listed in the brackets, and preceded by stringx and suffixed by stringy. The preceding string or the trailing string can be omitted, but the minimum number of strings within the braces, { }, is one, and items within the brackets must be separated by commas. Brace expansion can be used in situations like:

mkdir /home/ranga/docs/{school,work,personal}/mail
mkdir -p /usr/local/{bin,lib,man/man{1,2,3},etc}

These are equivalent to:

mkdir /home/ranga/docs/school/mail /home/ranga/docs/work/mail /home/ranga/docs/personal/mail
mkdir -p /usr/local/bin /usr/local/lib /usr/local/man/man1 
[CCC]/usr/local/man/man2 /usr/local/man/man3 /usr/local/etc

Brace expansion can be used in conjunction with pathname expansion, which allows for certain special characters to be given in pathnames. If these characters are given, the word in which they are present is regarded as a pattern and is replaced with a sorted list of filenames matching the pattern. The special characters are:
* Matches any string, including the null string.
? Matches any single character.
[...] Matches any one of the enclosed characters. A pair of characters separated by a minus sign denotes a range and any character between those two characters, is matched. For example [A-Z] matches all uppercase letters and [a-zA-Z] matches all letters.

Quoting

Quoting is used to disable the meaning of certain characters that the shell treats in a special way. Quoting can be used to make strings, by surrounding a set of characters with single quotes (') or double quotes ("). For example, the following are strings formed by quoting:

"Hello, I am a string"
'So am I'

Single characters can also be quoted if the character is preceded by a backslash (\). Some of the characters whose meanings are disabled by quoting are:

' ~ ! # $ % ^ & * ( ) - + =  \ | ; ` " , . < > ?

The basic quoting rules are a combination of sh and csh quoting rules. Strings enclosed in single quotes, `string', have all the special characters in them disabled. Strings enclosed in double quotes, "string", have all the special characters except !, $, ', \, and { disabled. Characters preceded by a backslash (\) will have their special meaning disabled.

In addition to quoting, bash recognizes several standard escape sequences familiar to C language programmers, such as \t for tab and \n for newline.

Simple Commands

Simple commands in bash are either variable assignments, single command names, or single command names whose output is redirected. " Bash treats the first word on the command line as the command to be executed and the rest of the words as arguments to the command, unless the command is preceded by variable assignments. A simple command will return its exit status, or 128+SIG if it is terminated by a signal of number SIG.

Pipelines

Several simple commands can be connected using pipes, forming a pipeline:

command1 | command2 | ...

The pipe character, |, connects the standard output of command1 to the standard input of command2, and so on. Each command is executed as a separate process. The exit status of the pipeline is the exit status of the last command. Examples of pipeline commands are:

tail -f /var/adm/messages | more
ps -ael | grep "$UID" | more
tar -cf - ./foo | { cd /tmp; tar -xf - }

In the first example the standard output of the tail command is piped into the standard input of the more command, which allows the output to be viewed one screen at a time. In the second example the standard output of ps is connected to the standard input of grep, and the standard output of grep is connected to the standard input of more, so that the output of grep can be viewed one screen at a time. The third example is an obscure but somewhat useful way for root to copy files without changing their ownership.

Lists

In addition to pipelines, commands can be executed in lists, where several pipelines are joined using the operators ;, &, &&, or ||. List can be used to execute commands and pipelines in sequence:

command1; command2; command3 ...
command1 | command2; command3 | command4 ...

Lists are commonly used for tasks such as:

lpr foo; lpq;
ps -ael | head -1; ps -ael | grep "$UID"

which cannot be executed in a single command.

Lists can also be used to execute commands based on the exit status of previous commands if the logical AND or logical OR operators are given:

command1 && command2
command1 || command2

If the logical AND operator is used, as in the first case, command2 will only be executed if command1 returns an exit status of 0. If the logical OR operator is used, as in the second case, command2 will only be executed if command1 returns with a nonzero exit status. The logical operators are often used instead of if statements; this form will seem intuitive to those familiar with the C programming language and its "lazy" logical operators.

A common use of the && operator is in situations like:

mkdir foo && cd foo

where a directory change is appropriate only if the directory was successfully created.

An example using the || operator is:

grep root /etc/passwd || echo "Help! No one in charge!"

Any list can be executed either in the current shell environment or in a subshell. Enclosing a list in braces:

{ list ; }

causes the list to be executed in the current shell, whereas enclosing a list in parentheses:

( list ; )

causes the list to be executed in a subshell. For example:

{ ps -ael | head -1; ps -ael | grep " $UID " ; } | more

runs the list, ps -ael | head -1; ps -ael | grep "$UID", in the current shell and then pipes the output to more. The same list could be run in a subshell:

( ps -ael | head -1; ps -ael | grep " $UID " ; ) | more

but this is probably not a good idea, since each subshell that runs takes up more system resources. It would be more efficient to run all the programs in the current shell. Running lists in subshells is useful because a subshell effectively makes all variables local and because subshells have their own working directories. This is illustrated by the following example:

pwd; ( cd /tmp ; pwd ) ; pwd;

The current working directory is only changed for the subshell.

Redirection

Lists can also include redirection of input and output using the < and > operators. By default < redirects the standard input and > redirects the standard output, but the redirection operators can also be used to open and close files. In general, output redirection is either:

command > file
list > file

The first form redirects the output of command to file, while the second redirects the output of list to file. For example:

date > now

redirects the output of the date into the file now. The output of lists can also be redirected:

{ date; uptime; who ; } > mylog

will redirect the output of all of the commands date, uptime, and who into the file mylog. In addition to this output redirection, which overwrites the output file, there is appended output redirection. Output will be appended to a file, or a file will be created if it doesn't exist if the >> operator is used. In general, appended output redirection is:

command >> file
list >> file

If the file mylog (above) shouldn't be erased each time data is added, the following could be used:

{ date; uptime; who ; } >> mylog

Bash also allows for the standard output and error to be redirected together. In general, this is done by:

&> file
>& file
> file 2>&1

The manual page states that the first form is preferred, but all three forms are equivalent. The third form works by using the file descriptors for standard output (file descriptor 1) and standard error (file descriptor 2); it effectively does the same thing as the dup2() C library function. A common situation where it is necessary to redirect both the standard output and the standard error is:

if type emacs &> /dev/null ; then
    EDITOR=emacs
else
    EDITOR=vi
fi

Here, only the return value of the type built-in command is of interest, its output or any error message it prints out are not, so they are redirected to /dev/null.

The input can also be redirected in a similar fashion. In general, input redirection is:

command < file

Here the contents of file will become the input to command. For example, the following is a common use of redirection:

Mail ranga@soda.berkeley.edu < Final_Exam_Answers

where the input to Mail, which becomes the body of the mail message, will be the file Final_Exam_Answers.

An additional use of input redirection is in the creation of Here documents. The general form for a Here document is:

command << delimiter
document
delimiter

The shell will interpret the << operator as a instruction to read input until a line containing the specified delimiter is found. All the input lines up to the line containing the delimiter are fed into the standard input of the command. Bash does not perform any kind of expansion on the given delimiter, but bash does perform expansion on the lines in the Here document. For example, to print out a quick list of URL's, the following Here document could be used:

lpr << MYURLS
http://www.csua.berkeley.edu/~ranga/
http://www.macintouch.com/
http://www.marathon.org/story/
http://www.gnu.org/
MYURLS

This provides a handy alternative to creating temporary files. In order to strip the tabs in this example the << operator can be given a - option.

Flow Control

Bash provides two powerful flow control mechanics, the if-fi block and the case-esac block. The if statement is normally used for the conditional execution of commands, whereas case statements allow any of a number of command sequences to be executed depending on which one of several pattern matches a variable first. It is often easier to write if statements as case statements if they involve matching a variable to a pattern.

The basic if syntax is if list ; then list; [elif list; then list; ] ... [else list;] fi, which is usually written as:

if list1 ; then
list2
elif list3 ; then
list4
else
list5
fi

Both the elif and the else statements are optional. An if statement can be written with any number of elif statements and if statements can contain just the if and elif statements. The lists in if statement can be lists of the form discussed in the previous section.

In the general if statement, shown above, first list1 is evaluated. If the exit code of list1 is 0, indicating a true condition, list2 is evaluated and the if statement exits. Otherwise, list3 is executed and then its exit code is checked. If list3 returns a 0, then list4 is executed and the if statement exits. If list3 does not return a 0, list5 is executed. A simple use of the if statement is:

if uuencode koala.gif koala.gif > koala.uu ; then
echo "Encoded koala.gif to koala.uu"
else
echo "Error encoding koala.gif"
fi

"Encoded koala.gif to koala.uu" will be echoed if the uuencode command's exit code indicates success. Otherwise, the error will be reported.

Most often the list given to an if statement will be one or more test commands, which can be invoked either by calling test as test expression or [ expression ]. The test command returns either a 0 (true) or a 1 (false) after evaluating an expression. The following is a list of the more commonly used option that test can be run with:
-d file True if file exists and is a directory.
-e file True if file exists.
-f file True if file exists and is a regular file.
-k file True if file exists has its ''sticky'' bit set.
-L file True if file exists and is a symbolic link.
-r file True if file exists and is readable.
-s file True if file exists and has a size greater than zero.
-t fd True if fd is opened on a terminal.
-w file True if file exists and is writable.
-x file True if file e exists and is executable.
-O file True if file exists and is owned by the effective user id.
file1 -nt file2 True if file1 is newer (according to modification date) than file2.
file1 -ot file2 True if file1 is older than file2.
-z string True if the length of string is zero.
-n string True if the length of string is non-zero.
string1 = string2
string1 == string2 True if the strings are equal.
string1 != string2 True if the strings are not equal.
! expr True if expr is false. The expr can be any of the test given above.
expr1 -a expr2 True if both expr1 AND expr2 are true.
expr1 -o expr2 True if either expr1 OR expr2 is true.
arg1 OP arg2 OP is one of -eq, -ne, -lt, -le, - gt, or - ge. These arithmetic binary operators return true if arg1 is equal to, not equal to, less than, less than or equal to, greater than, or greater than or equal to arg2, respectively. Arg1 and arg2 may be positive or negative integers.

Examples of common uses of a simple if statement in conjunction with test are:

if [ -d $HOME/bin ] ; then PATH="$PATH:$HOME/bin" ; fi
if [ -s $HOME/.bash_aliai ] ; then . $HOME/.bash_aliai ; fi

In the first example, test is used to determine whether a directory exists and then some action is taken. In the second example, test is used to determine whether a file exists and has non-zero size before any action is taken.

Here are two more equivalent examples that demonstrate how to combine tests:

if [ -z "$DTHOME" ] && [ -d /usr/dt ] ; then DTHOME=/usr/dt ; fi
if [ -z "$DTHOME" -a -d /usr/dt ] ; then DTHOME=/usr/dt ; fi

Some users prefer the first form because it is obvious what tests are being done and what the evaluation criteria are. Other users prefer the second form because it only invokes the [ command once and may be marginally more efficient.

The other form of flow control is the case-esac block. The basic syntax is case word in [ pattern [ | pattern ] ... ) list ;; ] ... esac, but it is usually written as:

case word in
pattern)
list
;;
pattern2)
list2
;;
esac

In this form, word is either a string or a variable, whose value is compared against each pattern until a match is found. The list following the matching pattern is executed. Once a list is executed, program flow jumps to the end of the entire case statement. If no matches are found, bash will exit the case statement. Some default actions can be performed by giving the * pattern, which matches anything. The number of patterns does not matter, but there must be at least one. The patterns can use the same special characters as patterns for pathname expansion, along with the OR operator, |. The ;; signifies to bash that the list has concluded and is similar to a break in the C programming language. An example of a simple case statement is:

case "$TERM" in
*term)
TERM=xterm
;;
network|dialup|unknown|vt[0-9]*)
TERM=vt100
;;
esac

Loops

Bash supports three types of loops: the for, while, and select loops. A for loop is used when a set of commands needs to simply be executed repeatedly. A while loop is used when a set of commands needs to be executed while a certain condition is true. A common use for a select loop is to provide a convenient selection interface.

The basic for loop syntax is for name [ in list1; ] do list2 ; done, but is usually written as:

for name in list1
do
list2
done

In the for loop, the variable name is set to each element in list1, and list2 is executed for each element of list1. If in list1 is not given, bash will iterate over the positional parameters. If list1 is given as a word, expansions will be performed on it. A simple for loop example is:

for i in 1 2 3 4 5 6 7 8 9 10
do
echo $i
done

A more common use of the for loop is:

for files in ~/.bash_*
do
echo "<HTML>" > ${files}.html
echo "<HEAD><TITLE>$files</TITLE></HEAD>" >> ${files}.html
echo "<BODY><PRE>" >> ${files}.html
cat $files >> ${files}.html
echo "</PRE></BODY>" >> ${files}.html
echo "</HTML>" >> ${files}.html
chmod guo+r ${files}.html
done

The basic while loop syntax is while list1 ; do list2 ; done. This is usually written as:

while list1
do
    list2
done

In the while loop, list1 is evaluated each time, and as long as it is true, list2 is executed. This allows for infinite loops to be written with /bin/true as list1. A simple while loop example is:

x=1
while [ $x -lt 10 ]
do
    echo $x
    x=$(($x+1))
done

This while loop copiesits input to its output, like the cat program:

while read
do
    echo $REPLY;
done

If input redirection is used, this loop will write the contents of the input file to the standard output, similar to cat.

A variation on the while loop is the until loop:

until list1
do
    list2
done

In the until loop, list2 is executed until list1 is true. The until loop is the same as a while loop where ! list1 is given. The following while and until loops are equivalent:

x=1; while ! [ $x -ge 10 ]
do
 echo $x
 x=$(($x+1))
done
x=1; until [ $x -ge 10 ]
do
 echo $x
 x=$(($x+1))
done

The select loop is an easy way to create a numbered menu from which users can select options. The basic select syntax is select name [ in list1; ] do list2 ; done, and is usually written as:

select name in list1
do
    list2
done

In the select loop, the items in list1 are printed onto the standard error preceded by a number. A prompt is then displayed and a line is read in. If $REPLY, the variable containing the value of the line that is read, contains a number of a displayed item, then list2 is executed. Otherwise, the list of items in list1 is displayed again. The select loop ends if an EOF (end of file) is read.

The following select loop displays a number list of the files in the directory /tmp and runs an ls -l on files that exist:

select file in /tmp/* QUIT
do
    if [ -e $file ] ; then
        ls -l $file
    else
        break
    fi
done

The output will be similar to:

1) /tmp/.                      6) /tmp/job.control.ms
2) /tmp/..                     7) /tmp/job.control.ps
3) /tmp/.X11-unix              8) /tmp/ps_data
4) /tmp/intro7.html            9) /tmp/sunpro.c.1.157.3.00
5) /tmp/java                  10) QUIT
#?

where #? is the prompt at which a number is typed by the user.

All loops in bash can be exited immediately by giving the built-in break command. This command also accepts as an argument an integer, greater or equal to 1, that indicates the number of levels to break out of. This feature is useful when nested loops are being used.

Comments

Bash comments start with the # character. Every character between the # and the newline is considered part of the comment and is ignored by bash. By default, this is true for both interactive shells and non-interactive shells. Interactive shells can change this behavior by using the shopt built-in function.

Initialization Files

An interactive login bash reads the files ~/.bash_profile, ~/.bash_login, ~/.profile, while a non-login interactive bash will read the file ~/.bashrc. The number of initialization files that bash tries to read is a source of confusion for many users.

Initialization File Considerations

One of the main problems confronting someone who is new to bash is which initialization files to create and what to put in them. The easiest solution, employed by many bash users, is to create one initialization file and have the other initialization files be symbolic links to that file.

Most users will create the ~/.bash_profile and then have the ~/.bashrc file be a link to the ~/.bash_profile, which insures that both login and non-login interactive bash shells have the same environment. The ~/.bash_profile is a better file to use as the initialization file than the ~/.bash_login, since bash tries to read the ~/.bash_profile first.

Some bash users, who use sh and ksh along with bash, use only the ~/.profile, and include a special section for bash, by using a test like:

if [ -n "$BASH" ] ; then ... (Bash specific code here) ... ; fi

In this case the ~/.bashrc will be a symbolic link to the ~/.profile. If bash is the only sh-like shell that will be used, it is probably best to create a ~/.bash_profile, because compatibility issues arising from the use of the ~/.profile can be avoided.

The most basic initialization files should set the file creation mask and $PATH, the search path for commands, since these are used by both interactive and non-interactive shells. Most initialization files will include much more, usually setting many variables and options for interactive shells only. Some common things that are done in initialization files for interactive shells are:

Some users also define aliases and functions inside their initialization files, but many choose to define these in another file called .bash_aliases or .bash_aliai. One of the reasons this is done is to avoid modifying a working initialization file every time an alias or function needs to be added. Since it is easy to source the file containing the aliases, using another file does not add complexity to the initialization file.

Shell Variables

The following section contains a list of some variables that bash uses and are often set in initialization files. Most of these variables are set in the sample initialization file given in a following section.

These variables are colon (:) separated lists of directory names that are used when searching for files:
$PATH The searchpath for commands, usually something like: PATH=/bin:/usr/bin:/usr/local/bin:/usr/ucb/
$MANPATH The manpage searchpath, usually something like: /usr/man:/usr/local/man
$LD_LIBRARY_PATH The library searchpath, usually something like: /lib:/usr/lib:/usr/local/lib:/usr/ucbinclude:/usr/ucblib
$CD_PATH The cd commands searchpath for directories, usually something like: .:~:~/docs
Theses variables pertain to default editor and are usually the name of the editor command, including options, to be run:
$EDITOR The name of default line editor (ed)
$VISUAL The name of the default visual editor (vi)
$FCEDIT The name of the editor for used with the fc built-in command

Bash uses these variables when checking for mail:
$MAIL The file where the users mail is stored, and the file that bash will check for mail in. It is usually preset to a value like: /usr/spool/mail/ranga
$MAILCHECK This variable's value is the time in seconds between mail checks. The default is 60. Bash will not check for mail if this variable is unset.
$MAIL_PATH A colon (:) separated list of files in which to check for mail.

These variables control how and where bash places commands in the history.
$HISTSIZE This variable's value is the number of commands to remember in the history. Its default value is 500.
$HISTFILE This variable contains the name of the file in which the history is saved. By default it is ~/.bash_history.
$HISTFILESIZE This variables value is the maximum number of lines to include in the history file. The default is 500.
$HISTCONTROL This variable can be set to three special values, ignorespace, ignoredups and ignoreboth. Other values have no effect. If it is set to ignorespace, commands starting with a space are not remembered by the history. If it is set to ignoredups, identical consecutive commands are not remembered in the history. Setting the variable to ignoreboth combines the other two option.

Bash also uses the following variables:
$TERM The terminal's type (e.g. xterm, vt100)
$PAGER The name of the default page by page viewer (e.g. less, more)
$PRINTER The name of the default printer
$IGNOREEOF This variable's value is the number of EOF characters that can be received as sole input, before bash exits. The default value is 10.
$TMOUT If set, this variable's value is the number of seconds to wait for input before terminating.
$FIGNORE This variable is a list of suffixes that ignores when completing a filename. It usually has a value like: .o:'~'

Bash also has several prompt variables, which control how the prompt is displayed. They are covered in the "Prompting" section.

Prompting

The prompt is the string displayed by bash when it is running interactively and is ready to read or complete a command. Bash supports four different prompts, $PS1, $PS2, $PS3 and $PS4. Of these, usually only the variables $PS1 and $PS2 are of interest in interactive shells. The primary prompt's string is the value of $PS1 and is displayed when bash is ready to read a command. The secondary prompt, $PS2, is displayed when bash needs more input to finish a command it has already started reading. The variable $PS3 contains the prompt issued when a select statement is issued. The variable $PS4 is displayed only during execution traces. Most users do not $PS3 or $PS4 very often and thus are not commonly set in initialization files. All the prompts are equally customizable and understand the following special character sequences:
\d The date in "Weekday Month Date" format ("Tue May 26")
\h The hostname up to the first dot (.)
\H The complete hostname
\s The name of the shell
\t The current time in 24-hour HH:MM:SS format
\T The current time in 12-hour HH:MM:SS format
\@ The current time in 12-hour am/pm format
\u The username of the current user
\v The version of bash (e.g., 2.00)
\V The release of bash, version + patchlevel (e.g., 2.00.0)
\w The current working directory
\W The basename of the current working directory
\! The history number of this command (this is the number of the current command as stored in the history file)
\# The command number of this command (this is the number of the current command since the shell was invoked, and is usually different than the history number)
\$ If the effective UID is 0, a #, otherwise a $

A few example of common values of $PS1 and corresponding sample prompts are:

PS1="\s-\v\$ "    bash-2.00$
PS1="\h \#\$ "    soda 2$
PS1="\h:\w [\#]\$ "    soda:~ [3]$
PS1="\t \H \#\$ "    19:10:21 soda.berkeley.edu 16$

In addition to these special character sequences, variables and commands can be included in the prompts, which are expanded in the manner discussed in the expansion section.

Bash also recognizes the variable $PROMPT_COMMAND, which can be set to the name of a command to be executed before a primary prompt is set. Some users like to have this set to display the load averages of the machine they are working on:

PROMPT_COMMAND="uptime | cut -d: -f4"

The value of $PROMPT_COMMAND can be set to any shell command, ranging from frivolous, PROMPT_COMMAND="fortune", to dangerous, PROMPT_COMMAND="/bin/rm -rf *".

set and shopt

The set and shopt built-in commands can be used to customize the behavior of bash. This customization is usually done in the initialization file. The set built-in is available in bash 1.x as well as in 2.0.x, whereas the shopt built-in is available only in bash 2.0.x.

The basic syntax of a set command is, set [+/-][options]. For example:

set -a
set -o allexport

sets the allexport option while

set +a
set +o allexport

unsets the allexport option.

The set command can be used interactively from the command line to change shell behavior, or it can be run in initialization files and shell scripts. The following is a list of useful options which are understood by the set command (equivalent options are given together):
-a
-o allexport These options force bash to automatically mark all variables for export when they are created or modified.
-b
-o notify These options force bash to report the status of terminated background jobs immediately, rather that waiting till the next primary prompt is issued.
-e
-o errexit If these options are given bash will exit as soon as a simple commands exits with non-zero status. The shell will not exit if the command is part of a loop, in a conditional list or if the return value of the command is being inverted with the ! operator.
-f
-o noglob These options disable pathname expansion.
-h
-o hash all These options force bash to remember thelocation of commands which are looked up forexecution. These options are enabled by default.
-n
-o noexec These option forces non-interactive bash shells to read commands without executing them, and is frequently used to check shell script syntax.
-v
-o verbose These options force bash to echo input lines after they are read. This option has a similar effect as the --verbose invocation option.
-B
-o braceexpand These options allow bash to perform brace expansion. These options are enabled by default.
-C
-o noclobber If these options are set, bash will not overwrite an existing file when redirection is performed. This can be overridden by using the >! operator.
-H
-o histexpand These options enable history substitution, and by default, are enabled for interactive shells.
-o emacs This option causes bash to use command line editing commands similar to those used in emacs. By default, this option is enabled for all interactive shells, except those invoked with the --noediting option.
-o history This option enables command history, and is enabled by default for interactive shells.
-o ignoreeof This option sets the value of $IGNOREEOF to 10.
-o posix This option forces bash into posix mode. This option has a effect similar to the --posix invocation option.
-o vi This option causes bash to use command line editing commands similar to those used in vi.
If set -o is given without any other arguments, a list of all the currently set options is listed. If set is given without any arguments, all currently set variables are listed.

The shopt built-in in bash 2.0.x allows for several other shell options to be set that affect bash's behavior. In general, a shell option is set by giving the shopt -s command, and a shell option is unset by giving the shopt -u command. For example:

shopt +s cdable_vars

enables the shell option cdable_vars while

shopt +u cable_vars

disables it. Some of the shell options that can be set are:
cdable_vars Setting this shell option allows the cd command to assume that a given argument, which is not a directory, is the name of a variable whose value is the name of directory to change to.
cdspell If this shell option is set, cd will correct minor errors in the spelling of a pathname and echo the correct pathname before changing directories.
checkhash If this shell option is set, bash will check to see if commands found in the hash table exist before trying to execute them. Commands that no longer exists are searched for in $PATH.
checkwinsize Setting this shell option forces bash to check the window size after each command is executed and, if necessary, update the values of LINES and COLUMNS.
cmdhist Setting this shell option allows bash to save all the lines of multiline commands in the same history entry.
lithist If this shell option and the shell option, cmdhist are set, multiline commands are saved in the history with the newlines.
histappend If this shell option is set, the history list is appended to the history file rather than overwriting it.
histverify Setting this option forces bash to reload a line for further editing after history substitutions have occurred.
dotglob If this shell option is set, bash includes filenames beginning with dots (.) in the results of pathname expansion.
hostcomplete If this shell option is set, bash will attempt to perform hostname completion when a word beginning with a @ is encountered. This shell option is enabled by default.
Interactive_comments Setting this shell option allows for comments to be entered in an interactive shell. This shell option is enabled by default.
mailwarn If this shell option is set, bash checks for new mail.
promptvars If this shell option is set, prompt strings undergo standard expansion. This option is enabled by default.
expand_aliases This shell option, if set, allows bash to expand aliases.

The shopt built-in also understands the options, -p, which lists all the shell options that are set, and -q, which suppresses output.

Aliases

Aliases are an easy way in interactive shells to shorten long commands, to do several commands by giving a single word, or to make sure that certain programs are always called with some handy options. In bash, aliases are created (set) by using the alias command and destroyed (unset) by using the unalias command. The basic syntax is similar to sh:

alias name=value

When bash encounters a command, it checks to see if the command contains known aliases. If it is does, those words are replaced by the corresponding alias text. The resulting text is checked for aliases also, but recursive substitution is not performed. Thus, if an alias such as:

alias rm="rm -i"

is declared, and rm is given as a command name, once rm -i is inserted, the rm in the resulting text is not expanded.

The simplest types of aliases are those which are used to slightly change how a command works.. For example, the following aliases simply executed the given command, with some default options each time:

alias m="more"
alias mv="mv -i"    # ask before overwriting a file when moving files
alias cp="cp -i""    # ask before overwriting a file when copying files
alias ls="ls -aFc""    # list all files and their file types in columns

Simple aliases are also handy for correcting common misspellings in command names:

alias chomd="chmod"
alias more="mroe"

Bash also supports more complex aliases like:

alias mq="/usr/lib/sendmail -bp" # print out the current mail queue
alias psme="{ ps -ael | head -1 ; ps -ael | grep \" ${UID} \" ; } | more"

Aliases can be overridden by preceding the alias name with a backslash (\), using the built-in command command or giving the full pathname to the executable. If rm is aliased to rm -i, each of the following will override that alias:

\rm
command rm
/bin/rm

Two features that bash aliases do not support are arguments and assignments. If an alias needs to use these, a shell function should be implemented.

Functions

Function serve a similar purpose as aliases, but are much more powerful and are akin to shell scripts. The basic syntax for defining a function is function name () { list; }. The keyword function is not required, and functions are frequently written as:

name () {
    list ;
}

When a function's name is given (without the parentheses), the associated list of commands is executed. The parentheses after the function's name, are used by the shell to identify functions and do not signify a null argument list.

The simplest functions are similar to aliases. For example, the following alias and function are equivalent:

alias mq="/usr/lib/sendmail -bp"
mq () { /usr/lib/sendmail -bp ; }

Functions are also used where small changes need to be made to the environment. For example, the following function changes the value of $IFS so that each directory in the path is printed on a separate line:

printpath ()
{
( IFS=:;
for i in $PATH;
do
echo $i;
done )
}

Functions are mostly used for more complex tasks where arguments are required. The simplest example of this is a function that only uses its first argument, $1:

mkcd ()
{
if [ -n "$1" ] && mkdir "$1"; then
cd "$1";
echo "Created" 'pwd';
fi
}

The following are examples of functions that process all the arguments given to them:

myuuencode ()
{
if [ -n "$1" ]; then
( for i in "$@";
do
if [ -e "$i" ] && uuencode $i $i >${i}.uu; then
echo "uuencoded $i to ${i}.uu";
else
echo "unable to uunencode $i";
fi;
done )
fi
}

murder ()
{
( for pattern in "$@";
do
ps -ef | grep "$pattern" | while read user pid junk; do
kill -HUP "$pid";
done;
done )
}

The first function is a handy way to uuencode files without having to type large commands, and the second function is a quick way of killing off processes that match certain patterns.

One important aspect of functions is that they can be called recursively. Since there is no limit on the amount of recursive calls that can be made, simply replacing an alias with a function like:

cp () { cp -i ; }

is not advised.

Sample Initialization File

This section contains a sample initialization file, .bash_profile.

# .bash_profile
# For versions 1.14.7 or higher
# set the user file creation mask
umask 022

# set PATH, the search path for commands
export PATH=/bin:/usr/bin:/usr/sbin:/usr/etc:/usr/ucb:/usr/local/bin

# Execute commands for interactive shells. Alternate test: if tty -s ; then
if [ -t ] ; then

    # set some terminal specific stuff
    stty -decctlq >& /dev/null
    stty erase '^H' kill '^U' intr '^C'

    # set TERM, the terminal type to vt100 if TERM is not "valid"
    case "$TERM" in
*term)
    TERM=xterm
    ;;
    network|dialup|unknown)
        TERM=vt100
        ;;
    esac
    export TERM

    # set MANPATH, the manual page search path
    MANPATH="/usr/man:/usr/local/man"

    if [ -d "$HOME/man" ] ; then
    MANPATH="$MANPATH:$HOME/man"
    fi

    export MANPATH

    # set  LD_LIBRARY_PATH, the library search path
    export LD_LIBRARY_PATH=/usr/lib:/usr/local/lib:/lib

    # set uid, required by some csh scripts (e.g.. which)
    export uid="$UID"

    # set CDPATH, the search path for cd
    export CDPATH=.:~:~/docs

    # set editing related variables
    if type emacs >/dev/null 2>&1; then
    EDITOR=emacs
    else
    EDITOR=vi
    fi
    FCEDIT="$EDITOR"
    VISUAL="$EDITOR"
    export EDITOR VISUAL FCEDIT

    # set PAGER, for viewing files, manpages etc...
    if type less >/dev/null 2>&1; then
    PAGER="less -s"
    else
    PAGER="more -s"
    fi
    export PAGER


    # set MAILCHECK, the delay between checking for mail
    export MAILCHECK=$((60*10))

    # set PS1 and PS2, the primary and secondary prompt strings
    PS1="\h \#\$ "
    PS2="\#> "
    export PS1 PS2

    # set history related variables
    export HISTCONTROL=ignoredups

    # set FIGNORE, suffixes for filename completion to ignore
    export FIGNORE=.out:.o:'~'

    # set options
    set -o notify ignoreeof nonclobber

    # set/unset optional shell behavior
    # available in v2.00.0 or newer
    if type shopt > /dev/null 2>&1 ; then
    shopt -s checkhash checkwinsize cmdhist lithist dotglob
    shopt -s histappend histreedit histverify

    if [ -n "$MAILCHECK" ] ; then
        shopt -s mailwarn
    fi

    fi

fi

Command Line and History

The following sections cover interacting with the command line. The key sequences discussed in this section assume that the default emacs keyboard setup is being used.

The Command Line

In interactive shells, commands are issued at the prompt. Bash accepts a line for execution when the accept-line characters, newline (\n) or return, are typed. For example when a command such as date is typed at the prompt in an interactive shell, bash splits it into simple commands with arguments. If the full pathname for a command is given, bash tries to run it; otherwise, bash first tries to find a shell function that matches the command, then a shell built-in, and, finally, a command located somewhere in the search path given by $PATH.

Command Line Editing

Often when commands are issued, typing mistakes occur. Bash provides a friendly interface to fix these mistakes, which is similar to ksh and tcsh. Using these key commands, users can move the cursor to any position on the command line and edit the text at that position.

In the following command descriptions, C- is used to indicate the control key (prefix) and M- is used to indicate the meta key (prefix); all other key strokes are literal. If two key commands are given, both must be used. For example, a keystroke written, C-f, means press the key marked f while holding down the control key and a keystroke written C-q C-v means press the key q while holding down the control key then press the key v while holding down the control key.

The following is a list of commands for moving around on the command line:
C-a Moves the cursor to the beginning of the command
C-e Moves the cursor to the end of the command
C-f Moves the cursor forward a character. On terminals/keyboards that support the arrow keys, the right arrow can be used to move the cursor forward one character.
C-b Moves the cursor back a character. On terminal/keyboards that support the arrow keys, the left arrow can be used to move the cursor forward one character.
M-f Moves the cursor forward one word.
M-b Moves the cursor back one word.
C-l Clears the screen and puts the current line at the top of the screen. This is equivalent to the clear command.

Bash allows for substantial editing of commands by deleting, killing, and yanking text. Kill and yank are similar to the cut and paste features in most word processors. These commands are:
delete Deletes the character behind the cursor.
C-d Deletes the character under the cursor. If there is no character under the cursor, a EOF character is printed
C-k Kills all the text from the cursors position to the end of the line.
C-x delete Kills all the text on the line.
C-u Kills all the text between the cursor and the beginning of the line.
C-y Yanks killed text.

Bash also supports the following editing related commands:
M-u Changes the current word to Uppercase.
M-l Changes the current word to Lowercase
C-q, C-v Adds the next character that is typed verbatim.
C-g Aborts editing
C-_
C-x C-u Undo
M-r Undoes all changes made to the current line
C-] Reads a character and moves the cursor to the next occurrence of that character.
M-C-] Reads a character and moves the cursor to the previous occurrence of that character.

In some instances, editing multiline commands, for one, the command line editing tools are not sufficient. For these cases, bash provides the fix command built-in, fc. The fc command invokes an editor for command editing and runs the edited command once the editor exits. By default, fc invokes the editor and inserts the last command that was executed into the editor's buffer, but fc can be told which command number needs to be fixed along with the editor that should be used. For example:

fc 10

will invoke an editor on the tenth command executed since the shell began, while:

fc -10

will invoke an editor on a command that was executed 10 commands ago. The editor invoked can be specified by giving the -e option followed by the name of an editor. For example:

fc -e emacs

will invoke emacs, and place the last command in the buffer for editing. If the -e option is not given, the value of $FCEDIT, if it exists, is used as the name of the editor; otherwise, the value of $EDITOR is used as the name of the editor. If neither $FCEDIT or $EDITOR are set, vi is used as the editor.

Completion

In addition to editing commands, bash also completes text before the cursor. Bash has the ability to complete variable names, usernames, hostnames, commands, and filenames. For words beginning with a $, variable completion is attempted. For words beginning with a ~, bash username completion is attempted. For words beginning with a @, hostname completion is attempted. For other words, command completion is first attempted, followed by filename completion. The general completion command is given every time the tab key is pressed. Some of the other completion commands are:
M-? Lists all possible completions
M-* Inserts a string containing all possible completions at the cursor
M-/ Forces bash to attempt filename completion
M-~ Forces bash to attempt username completion
M-$ Forces bash to attempt variable completion
M-@ Forces bash to attempt hostname completion
M-! Forces bash to attempt command completion. Bash will attempt to find completions that are aliases, reserved words, shell functions, built-ins, and executables.

History Recall

The history is the list of commands that have been previously executed. By default, bash stores up to 500 commands in its history, starting at command 1 and incrementing by 1 each time a command is accepted. Once a command has been accepted (for instance, a return was typed) it is appended to the history file, ~/.bash_history. There are several variables, discussed in the section on shell variables, that can be used to customize this behavior. In addition to storing commands in the history, bash recognizes the following commands for recalling previously executed commands:
C-p Recalls the previous command in the history. On keyboards/terminals that support arrow keys, the up arrow key can be used to recall the previous command.
C-n Recalls the next command in the history. On keyboards/terminals that support arrow keys, the down arrow key can be used to recall the next command.
M-< Moves to the first command in the history.
M-> Moves to the last command in the history.
C-r Searches through the history starting at the current line and moving backwards (looks through previous commands). This can be slow if the history is large.
C-s Searches through the history starting at the current line and moving forward.

History Variables

Bash maintains several read only special parameters that relate to commands that have been executed. These are:
$? This parameter's value is the exit value of the most recently executed pipeline.
$$ This parameter's value is the process number of the current shell.
$! This parameter's value is the process number of the most recently executed background process.
$0 This is the name of the current shell or its reference in a shell script; it is the name with which the script was invoked.

History Substitution

On many occasions, recalling and editing commands may not be easy. For such instances, bash provides for history manipulation (substitution) using operators similar to csh. History substitution begins when bash encounters the ! operator, and it is not followed by a blank, = or (. If the ! operator is followed by these characters, bash performs negation rather than history substitution.

A simple history substitution is the ! operator followed by the command number of the command that needs to be reexecuted. For example:

!213

will execute the command stored in the history with the command number of 213. In general, this history substitution is referred to as !n, where n is the command number. If n is negative, the command issued n commands before the current command is recalled and executed. For example the following:

!-10

will execute the command that was given 10 commands ago, and the last command executed can be recalled with:

!-1

Since recalling the previous command is a common task, bash provides the !! shorthand for it. In addition, a command can be recalled from the history by giving part of a string that it starts with or contains. For example, the last mail command can be recalled by:

!mail

The general syntax for recalling commands that start with a particular string is !string. In the case of the previous example, this history substitution will not recall commands that contain the string mail in them, like sendmail or rmail, but it will recall the command mailtool, if it appears before the command mail in the history. Bash can recall commands that contain particular strings if the history substitution is of the form !?string?. For example:

!?host?

can be used to recall the commands hostname, gethostname, ypcat hosts, and echo $host.

Sometimes simply recalling commands is not enough, so bash supports several partial recall and replace history substitutions. The simplest of these is the ^string1^string2^ replacement, which replaces string1 in the previous command with string2. If the command:

xv koala.gif

is executed, and then the picture kangaroo.gif needs to be viewed, the following quick substitution can be employed:

^koala^kangaroo^

The more advanced replacement operations can recall arguments given to previous commands. The simplest of these is the !:0, which recalls the previous command name. For example if the command:

cpwd () { if [ -n "$1" ] ; then cd "$1"; fi ; pwd }

is given, and the formatted version needs to be viewed with the type command, the following command will produce the desired result:

type !:0

Once the formatted function is in view, it may need to be used, so the following command:

!:^ /bin

will recall the name of the function, which was given as the first argument to type, since !:^ substitution recalls the first argument given to a command. Sometimes the last argument given to command will need to be recalled, so the !$ or the !:$ substitution can be used. These operators are a shorthand for the more general !:n substitution, which recalls the nth argument given to a command. If the third argument from a command were required, the !:3 substitution would be used.

Often it is not enough to just recall one argument to a command. For example, if the command:

xman -background white -geometry 135x55

is executed, and a parameter of the same background color and geometry were desired, the substitution:

perfmeter !*

would produce the desired results, since the !* and the !:* substitutions are used to recall the entire list of arguments given to a command. These substitutions are special forms of the !:x-y history substitutions, which recall all the arguments between x and y that are given to a command. For example, if the following command is given:

javac -d . -O rectPanel.java

and the second and third arguments (the compiler flags) are required for a subsequent compile, the substitution:

javac !:1-3 javaStarDate.java

will produce the desired result.

As a general rule, history substitutions can be intermixed, and can be written as !string:option, where option is the previously discussed options, 0, n, ^, $, x-y and *. For example, if the following finger command is run:

finger ranga@soda.berkeley.edu sriranga@ocf.berkeley.edu

and, after several other commands have been executed, mail needs to be sent to those users, the following history substitution:

mail !finger:*

will produce the proper result.

Summary

Bash is a powerful shell, popular with both beginning and advanced users, since it provides many user-oriented features, such as command line editing and file name completion. Its power and popularity are also due to its integration of features found in the Bourne Shell, the Korn Shell, and the C Shell. Bash is also available on many non-UNIX systems, so learning and using Bash is a valuable skill for everyone.

References

The following sources were referred.

Manual Page BASH(1),

Free Software Foundation, 1996

BASH FAQ version 2.1, 1996

TOCBACKFORWARDHOME


©Copyright, Macmillan Computer Publishing. All rights reserved.