LDP/LDP/guide/docbook/Bash-Beginners-Guide/chap3.xml

1102 lines
76 KiB
XML

<chapter id="chap_03"><title>The Bash environment</title>
<abstract>
<para>In this chapter we will discuss the various ways in which the Bash environment can be influenced:</para>
<para><itemizedlist>
<listitem><para>Editing shell initialization files</para></listitem>
<listitem><para>Using variables</para></listitem>
<listitem><para>Using different quote styles</para></listitem>
<listitem><para>Perform arithmetic calculations</para></listitem>
<listitem><para>Assigning aliases</para></listitem>
<listitem><para>Using expansion and substitution</para></listitem>
</itemizedlist></para>
</abstract>
<sect1 id="sect_03_01"><title>Shell initialization files</title>
<sect2 id="sect_03_01_01"><title>System-wide configuration files</title>
<sect3 id="sect_03_01_01_01"><title>/etc/profile</title>
<para>When invoked interactively with the <option>--login</option> option or when invoked as <command>sh</command>, Bash reads the <filename>/etc/profile</filename> instructions. These usually set the shell variables <varname>PATH</varname>, <varname>USER</varname>, <varname>MAIL</varname>, <varname>HOSTNAME</varname> and <varname>HISTSIZE</varname>.</para>
<para>On some systems, the <command>umask</command> value is configured in <filename>/etc/profile</filename>; on other systems this file holds pointers to other configuration files such as:</para>
<itemizedlist>
<listitem><para><filename>/etc/inputrc</filename>, the system-wide Readline initialization file where you can configure the command line bell-style.</para></listitem>
<listitem><para>the <filename>/etc/profile.d</filename> directory, which contains files configuring system-wide behavior of specific programs.</para></listitem>
</itemizedlist>
<para>All settings that you want to apply to all your users' environments should be in this file. It might look like this:</para>
<screen>
# /etc/profile
# System wide environment and startup programs, for login setup
PATH=$PATH:/usr/X11R6/bin
# No core files by default
ulimit -S -c 0 &gt; /dev/null 2&gt;&amp;1
USER="`id -un`"
LOGNAME=$USER
MAIL="/var/spool/mail/$USER"
HOSTNAME=`/bin/hostname`
HISTSIZE=1000
# Keyboard, bell, display style: the readline config file:
if [ -z "$INPUTRC" -a ! -f "$HOME/.inputrc" ]; then
INPUTRC=/etc/inputrc
fi
PS1="\u@\h \W"
export PATH USER LOGNAME MAIL HOSTNAME HISTSIZE INPUTRC PS1
# Source initialization files for specific programs (ls, vim, less, ...)
for i in /etc/profile.d/*.sh ; do
if [ -r "$i" ]; then
. $i
fi
done
# Settings for program initialization
source /etc/java.conf
export NPX_PLUGIN_PATH="$JRE_HOME/plugin/ns4plugin/:/usr/lib/netscape/plugins"
PAGER="/usr/bin/less"
unset i
</screen>
<para>This configuration file sets some basic shell environment variables as well as some variables required by users running Java and/or Java applications in their web browser. See <xref linkend="sect_03_02" />.</para>
<para>See <xref linkend="chap_07" /> for more on the conditional <command>if</command> used in this file; <xref linkend="chap_09" /> discusses loops such as the <command>for</command> construct.</para>
<para>The Bash source contains sample <filename>profile</filename> files for general or individual use. These and the one in the example above need changes in order for them to work in your environment!</para>
</sect3>
<sect3 id="sect_03_01_01_02"><title>/etc/bashrc</title>
<para>On systems offering multiple types of shells, it might be better to put Bash-specific configurations in this file, since <filename>/etc/profile</filename> is also read by other shells, such as the Bourne shell. Errors generated by shells that don't understand the Bash syntax are prevented by splitting the configuration files for the different types of shells. In such cases, the user's <filename>~/.bashrc</filename> might point to <filename>/etc/bashrc</filename> in order to include it in the shell initialization process upon login.</para>
<para>You might also find that <filename>/etc/profile</filename> on your system only holds shell environment and program startup settings, while <filename>/etc/bashrc</filename> contains system-wide definitions for shell functions and aliases. The <filename>/etc/bashrc</filename> file might be referred to in <filename>/etc/profile</filename> or in individual user shell initialization files.</para>
<para>The source contains sample <filename>bashrc</filename> files, or you might find a copy in <filename>/usr/share/doc/bash-2.05b/startup-files</filename>. This is part of the <filename>bashrc</filename> that comes with the Bash documentation:</para>
<screen>
alias ll='ls -l'
alias dir='ls -ba'
alias c='clear'
alias ls='ls --color'
alias mroe='more'
alias pdw='pwd'
alias sl='ls --color'
pskill()
{
local pid
pid=$(ps -ax | grep $1 | grep -v grep | gawk '{ print $1 }')
echo -n "killing $1 (process $pid)..."
kill -9 $pid
echo "slaughtered."
}
</screen>
<para>Apart from general aliases, it contains useful aliases which make commands work even if you misspell them. We will discuss aliases in <xref linkend="sect_03_05_02" />. This file contains a function, <command>pskill</command>; functions will be studied in detail in <xref linkend="chap_11" />.</para>
</sect3>
</sect2>
<sect2 id="sect_03_01_02"><title>Individual user configuration files</title>
<note><title>I don't have these files?!</title><para>These files might not be in your home directory by default; create them if needed.</para></note>
<sect3 id="sect_03_01_02_01"><title>~/.bash_profile</title>
<para>This is the preferred configuration file for configuring user environments individually. In this file, users can add extra configuration options or change default settings:</para>
<screen>
<prompt>franky~&gt;</prompt> <command>cat <filename>.bash_profile</filename></command>
#################################################################
# #
# .bash_profile file #
# #
# Executed from the bash shell when you log in. #
# #
#################################################################
source ~/.bashrc
source ~/.bash_login
case "$OS" in
IRIX)
stty sane dec
stty erase
;;
# SunOS)
# stty erase
# ;;
*)
stty sane
;;
esac
</screen>
<para>This user configures the backspace character for login on different operating systems. Apart from that, the user's <filename>.bashrc</filename> and <filename>.bash_login</filename> are read.</para>
</sect3>
<sect3 id="sect_03_01_02_02"><title>~/.bash_login</title>
<para>This file contains specific settings that are normally only executed when you log in to the system. In the example, we use it to configure the <command>umask</command> value and to show a list of connected users upon login. This user also gets the calendar for the current month:</para>
<screen>
#######################################################################
# #
# Bash_login file #
# #
# commands to perform from the bash shell at login time #
# (sourced from .bash_profile) #
# #
#######################################################################
# file protection
umask 002 # all to me, read to group and others
# miscellaneous
w
cal `date +"%m"` `date +"%Y"`
</screen>
<para>In the absence of <filename>~/.bash_profile</filename>, this file will be read.</para>
</sect3>
<sect3 id="sect_03_01_02_03"><title>~/.profile</title>
<para>In the absence of <filename>~/.bash_profile</filename> and <filename>~/.bash_login</filename>, <filename>~/.profile</filename> is read. It can hold the same configurations, which are then also accessible by other shells. Mind that other shells might not understand the Bash syntax.</para>
</sect3>
<sect3 id="sect_03_01_02_04"><title>~/.bashrc</title>
<para>Today, it is more common to use a non-login shell, for instance when logged in graphically using X terminal windows. Upon opening such a window, the user does not have to provide a user name or password; no authentication is done. Bash searches for <filename>~/.bashrc</filename> when this happens, so it is referred to in the files read upon login as well, which means you don't have to enter the same settings in multiple files.</para>
<para>In this user's <filename>.bashrc</filename> a couple of aliases are defined and variables for specific programs are set after the system-wide <filename>/etc/bashrc</filename> is read:</para>
<screen>
<prompt>franky ~&gt;</prompt> <command>cat <filename>.bashrc</filename></command>
# /home/franky/.bashrc
# Source global definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi
# shell options
set -o noclobber
# my shell variables
export PS1="\[\033[1;44m\]\u \w\[\033[0m\] "
export PATH="$PATH:~/bin:~/scripts"
# my aliases
alias cdrecord='cdrecord -dev 0,0,0 -speed=8'
alias ss='ssh octarine'
alias ll='ls -la'
# mozilla fix
MOZILLA_FIVE_HOME=/usr/lib/mozilla
LD_LIBRARY_PATH=/usr/lib/mozilla:/usr/lib/mozilla/plugins
MOZ_DIST_BIN=/usr/lib/mozilla
MOZ_PROGRAM=/usr/lib/mozilla/mozilla-bin
export MOZILLA_FIVE_HOME LD_LIBRARY_PATH MOZ_DIST_BIN MOZ_PROGRAM
# font fix
alias xt='xterm -bg black -fg white &amp;'
# BitchX settings
export IRCNAME="frnk"
# THE END
<prompt>franky ~&gt;</prompt>
</screen>
<para>More examples can be found in the Bash package. Remember that sample files might need changes in order to work in your environment.</para>
<para>Aliases are discussed in <xref linkend="sect_03_05" />.</para>
</sect3>
<sect3 id="sect_03_01_02_05"><title>~/.bash_logout</title>
<para>This file contains specific instructions for the logout procedure. In the example, the terminal window is cleared upon logout. This is useful for remote connections, which will leave a clean window after closing them.</para>
<screen>
<prompt>franky ~&gt;</prompt> <command>cat <filename>.bash_logout</filename></command>
#######################################################################
# #
# Bash_logout file #
# #
# commands to perform from the bash shell at logout time #
# #
#######################################################################
clear
<prompt>franky ~&gt;</prompt>
</screen>
</sect3>
</sect2>
<sect2 id="sect_03_01_03"><title>Changing shell configuration files</title>
<para>When making changes to any of the above files, users have to either reconnect to the system or <command>source</command> the altered file for the changes to take effect. By interpreting the script this way, changes are applied to the current shell session:</para>
<figure><title>Different prompts for different users</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/prompt.eps" format="EPS"></imagedata>
</imageobject>
<imageobject>
<imagedata fileref="images/prompt.png" format="PNG"></imagedata>
</imageobject>
<textobject>
<phrase>Prompt 1 is "franky ~&gt;; prompt is then changed with "export PS1="\[\033[1;42m\]$USER is in \w\[\033[0m\] " resulting in a green reverse video prompt displaying username and present working directory in humanly readable form. Prompt for root is set using a similar PS1 configuration to be bright red reverse video.</phrase>
</textobject>
</mediaobject>
</figure>
<para>Most shell scripts execute in a private environment: variables are not inherited by child processes unless they are exported by the parent shell. Sourcing a file containing shell commands is a way of applying changes to your own environment and setting variables in the current shell.</para>
<para>This example also demonstrates the use of different prompt settings by different users. In this case, red means danger. When you have a green prompt, don't worry too much.</para>
<para>Note that <command>source <filename>resourcefile</filename></command> is the same as <command>. <filename>resourcefile</filename></command>.</para>
<para>Should you get lost in all these configuration files, and find yourself confronted with settings of which the origin is not clear, use <command>echo</command> statements, just like for debugging scripts; see <xref linkend="sect_02_03_02" />. You might add lines like this:</para>
<screen>
echo "Now executing .bash_profile.."
</screen>
<para>or like this:</para>
<screen>
echo "Now setting PS1 in .bashrc:"
export PS1="[some value]"
echo "PS1 is now set to $PS1"
</screen>
</sect2>
</sect1>
<sect1 id="sect_03_02"><title>Variables</title>
<sect2 id="sect_03_02_01"><title>Types of variables</title>
<para>As seen in the examples above, shell variables are in uppercase
characters by convention. Bash keeps a list of two types of variables:</para>
<sect3 id="sect_03_02_01_01"><title>Global variables</title>
<para>Global variables or environment variables are available in all shells. The <command>env</command> or <command>printenv</command> commands can be used to display environment variables. These programs come with the <emphasis>sh-utils</emphasis> package.</para>
<para>Below is a typical output:</para>
<screen>
<prompt>franky ~&gt;</prompt> <command>printenv</command>
CC=gcc
CDPATH=.:~:/usr/local:/usr:/
CFLAGS=-O2 -fomit-frame-pointer
COLORTERM=gnome-terminal
CXXFLAGS=-O2 -fomit-frame-pointer
DISPLAY=:0
DOMAIN=hq.xalasys.com
e=
TOR=vi
FCEDIT=vi
FIGNORE=.o:~
G_BROKEN_FILENAMES=1
GDK_USE_XFT=1
GDMSESSION=Default
GNOME_DESKTOP_SESSION_ID=Default
GTK_RC_FILES=/etc/gtk/gtkrc:/nethome/franky/.gtkrc-1.2-gnome2
GWMCOLOR=darkgreen
GWMTERM=xterm
HISTFILESIZE=5000
history_control=ignoredups
HISTSIZE=2000
HOME=/nethome/franky
HOSTNAME=octarine.hq.xalasys.com
INPUTRC=/etc/inputrc
IRCNAME=franky
JAVA_HOME=/usr/java/j2sdk1.4.0
LANG=en_US
LDFLAGS=-s
LD_LIBRARY_PATH=/usr/lib/mozilla:/usr/lib/mozilla/plugins
LESSCHARSET=latin1
LESS=-edfMQ
LESSOPEN=|/usr/bin/lesspipe.sh %s
LEX=flex
LOCAL_MACHINE=octarine
LOGNAME=franky
LS_COLORS=no=00:fi=00:di=01;34:ln=01;36:pi=40;33:so=01;35:bd=40;33;01:cd=40;33;01:or=01;05;37;41:mi=01;05;37;41:ex=01;32:*.cmd=01;32:*.exe=01;32:*.com=01;32:*.btm=01;32:*.bat=01;32:*.sh=01;32:*.csh=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.bz=01;31:*.tz=01;31:*.rpm=01;31:*.cpio=01;31:*.jpg=01;35:*.gif=01;35:*.bmp=01;35:*.xbm=01;35:*.xpm=01;35:*.png=01;35:*.tif=01;35:
MACHINES=octarine
MAILCHECK=60
MAIL=/var/mail/franky
MANPATH=/usr/man:/usr/share/man/:/usr/local/man:/usr/X11R6/man
MEAN_MACHINES=octarine
MOZ_DIST_BIN=/usr/lib/mozilla
MOZILLA_FIVE_HOME=/usr/lib/mozilla
MOZ_PROGRAM=/usr/lib/mozilla/mozilla-bin
MTOOLS_FAT_COMPATIBILITY=1
MYMALLOC=0
NNTPPORT=119
NNTPSERVER=news
NPX_PLUGIN_PATH=/plugin/ns4plugin/:/usr/lib/netscape/plugins
OLDPWD=/nethome/franky
OS=Linux
PAGER=less
PATH=/nethome/franky/bin.Linux:/nethome/franky/bin:/usr/local/bin:/usr/local/sbin:/usr/X11R6/bin:/usr/bin:/usr/sbin:/bin:/sbin:.
PS1=\[\033[1;44m\]franky is in \w\[\033[0m\]
PS2=More input>
PWD=/nethome/franky
SESSION_MANAGER=local/octarine.hq.xalasys.com:/tmp/.ICE-unix/22106
SHELL=/bin/bash
SHELL_LOGIN=--login
SHLVL=2
SSH_AGENT_PID=22161
SSH_ASKPASS=/usr/libexec/openssh/gnome-ssh-askpass
SSH_AUTH_SOCK=/tmp/ssh-XXmhQ4fC/agent.22106
START_WM=twm
TERM=xterm
TYPE=type
USERNAME=franky
USER=franky
_=/usr/bin/printenv
VISUAL=vi
WINDOWID=20971661
XAPPLRESDIR=/nethome/franky/app-defaults
XAUTHORITY=/nethome/franky/.Xauthority
XENVIRONMENT=/nethome/franky/.Xdefaults
XFILESEARCHPATH=/usr/X11R6/lib/X11/%L/%T/%N%C%S:/usr/X11R6/lib/X11/%l/%T/%N%C%S:/usr/X11R6/lib/X11/%T/%N%C%S:/usr/X11R6/lib/X11/%L/%T/%N%S:/usr/X11R6/lib/X11/%l/%T/%N%S:/usr/X11R6/lib/X11/%T/%N%S
XKEYSYMDB=/usr/X11R6/lib/X11/XKeysymDB
XMODIFIERS=@im=none
XTERMID=
XWINHOME=/usr/X11R6
X=X11R6
YACC=bison -y
</screen>
</sect3>
<sect3 id="sect_03_02_01_02"><title>Local variables</title>
<para>Local variables are only available in the current shell. Using the <command>set</command> built-in command without any options will display a list of all variables (including environment variables) and functions. The output will be sorted according to the current locale and displayed in a reusable format.</para>
<para>Below is a diff file made by comparing <command>printenv</command> and <command>set</command> output, after leaving out the functions which are also displayed by the <command>set</command> command:</para>
<screen>
<prompt>franky ~&gt;</prompt> <command>diff <filename>set.sorted</filename> <filename>printenv.sorted</filename></command> | <command>grep <parameter>"&lt;"</parameter></command> | <command>awk <parameter>'{ print $2 }'</parameter></command>
BASE=/nethome/franky/.Shell/hq.xalasys.com/octarine.aliases
BASH=/bin/bash
BASH_VERSINFO=([0]="2"
BASH_VERSION='2.05b.0(1)-release'
COLUMNS=80
DIRSTACK=()
DO_FORTUNE=
EUID=504
GROUPS=()
HERE=/home/franky
HISTFILE=/nethome/franky/.bash_history
HOSTTYPE=i686
IFS=$'
LINES=24
MACHTYPE=i686-pc-linux-gnu
OPTERR=1
OPTIND=1
OSTYPE=linux-gnu
PIPESTATUS=([0]="0")
PPID=10099
PS4='+
PWD_REAL='pwd
SHELLOPTS=braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor
THERE=/home/franky
UID=504
</screen>
<note><title>Awk</title><para>the GNU Awk programming language is explained in <xref linkend="chap_06" />.</para></note>
</sect3>
<sect3 id="sect_03_02_01_03"><title>Variables by content</title>
<para>Apart from dividing variables in local and global variables, we can also divide them in categories according to the sort of content the variable contains. In this respect, variables come in 4 types:</para>
<itemizedlist>
<listitem><para>String variables</para></listitem>
<listitem><para>Integer variables</para></listitem>
<listitem><para>Constant variables</para></listitem>
<listitem><para>Array variables</para></listitem>
</itemizedlist>
<para>We'll discuss these types in <xref linkend="chap_10" />. For now, we will work with integer and string values for our variables.</para>
</sect3>
</sect2>
<sect2 id="sect_03_02_02"><title>Creating variables</title>
<para>Variables are case sensitive and capitalized by default. Giving local variables a lowercase name is a convention which is sometimes applied. However, you are free to use the names you want or to mix cases. Variables can also contain digits, but a name starting with a digit is not allowed:</para>
<screen>
<prompt>prompt&gt;</prompt> <command>export <varname>1number</varname>=<parameter>1</parameter></command>
bash: export: `1number=1': not a valid identifier
</screen>
<para>To set a variable in the shell, use</para>
<cmdsynopsis><command><varname>VARNAME</varname>="value"</command></cmdsynopsis>
<para>Putting spaces around the equal sign will cause errors. It is a good habit to quote content strings when assigning values to variables: this will reduce the chance that you make errors.</para>
<para>Some examples using upper and lower cases, numbers and spaces:</para>
<screen>
<prompt>franky ~&gt;</prompt> <command><varname>MYVAR1</varname>=<parameter>"2"</parameter></command>
<prompt>franky ~7gt;</prompt> <command>echo <varname>$MYVAR1</varname></command>
2
<prompt>franky ~&gt;</prompt> <command><varname>first_name</varname>=<parameter>"Franky"</parameter></command>
<prompt>franky ~&gt;</prompt> <command>echo <varname>$first_name</varname></command>
Franky
<prompt>franky ~&gt;</prompt> <command><varname>full_name</varname>=<parameter>"Franky M. Singh"</parameter></command>
<prompt>franky ~&gt;</prompt> <command>echo <varname>$full_name</varname></command>
Franky M. Singh
<prompt>franky ~&gt;</prompt> <command><varname>MYVAR-2</varname>=<parameter>"2"</parameter></command>
bash: MYVAR-2=2: command not found
<prompt>franky ~&gt;</prompt> <command><varname>MYVAR1</varname> =<parameter>"2"</parameter></command>
bash: MYVAR1: command not found
<prompt>franky ~&gt;</prompt> <command><varname>MYVAR1</varname>= <parameter>"2"</parameter></command>
bash: 2: command not found
<prompt>franky ~&gt;</prompt> <command>unset <varname>MYVAR1</varname> <varname>first_name</varname> <varname>full_name</varname></command>
<prompt>franky ~&gt;</prompt> <command>echo <varname>$MYVAR1</varname> <varname>$first_name</varname> <varname>$full_name</varname></command>
&lt;--no output--&gt;
<prompt>franky ~&gt;</prompt>
</screen>
</sect2>
<sect2 id="sect_03_02_03"><title>Exporting variables</title>
<para>A variable created like the ones in the example above is only available to the current shell. It is a local variable: child processes of the current shell will not be aware of this variable. In order to pass variables to a subshell, we need to <emphasis>export</emphasis> them using the <command>export</command> built-in command. Variables that are exported are referred to as environment variables. Setting and exporting is usually done in one step:</para>
<cmdsynopsis><command>export <varname>VARNAME</varname>="<parameter>value</parameter>"</command></cmdsynopsis>
<para>A subshell can change variables it inherited from the parent, but the changes made by the child don't affect the parent. This is demonstrated in the example:</para>
<screen>
<prompt>franky ~&gt;</prompt> <command><varname>full_name</varname>=<parameter>"Franky M. Singh"</parameter></command>
<prompt>franky ~&gt;</prompt> <command>bash</command>
<prompt>franky ~&gt;</prompt> <command>echo <varname>$full_name</varname></command>
<prompt>franky ~&gt;</prompt> <command>exit</command>
<prompt>franky ~&gt;</prompt> <command>export <varname>full_name</varname></command>
<prompt>franky ~&gt;</prompt> <command>bash</command>
<prompt>franky ~&gt;</prompt> <command>echo <varname>$full_name</varname></command>
Franky M. Singh
<prompt>franky ~&gt;</prompt> <command>export <varname>full_name</varname>=<parameter>"Charles the Great"</parameter></command>
<prompt>franky ~&gt;</prompt> <command>echo <varname>$full_name</varname></command>
Charles the Great
<prompt>franky ~&gt;</prompt> <command>exit</command>
<prompt>franky ~&gt;</prompt> <command>echo <varname>$full_name</varname></command>
Franky M. Singh
<prompt>franky ~&gt;</prompt>
</screen>
<para>When first trying to read the value of <varname>full_name</varname> in a subshell, it is not there (<command>echo</command> shows a null string). The subshell quits, and <varname>full_name</varname> is exported in the parent - a variable can be exported after it has been assigned a value. Then a new subshell is started, in which the variable exported from the parent is visible. The variable is changed to hold another name, but the value for this variable in the parent stays the same.</para>
</sect2>
<sect2 id="sect_03_02_04"><title>Reserved variables</title>
<sect3 id="sect_03_02_04_01"><title>Bourne shell reserved variables</title>
<para>Bash uses certain shell variables in the same way as the Bourne shell. In some cases, Bash assigns a default value to the variable. The table below gives an overview of these plain shell variables:</para>
<table id="table_03_01" frame="all"><title>Reserved Bourne shell variables</title>
<tgroup cols="2" align="left" colsep="1" rowsep="1">
<thead>
<row><entry>Variable name</entry><entry>Definition</entry></row>
</thead>
<tbody>
<row><entry>CDPATH</entry><entry>A colon-separated list of directories used as a search path for the <command>cd</command> built-in command.</entry></row>
<row><entry>HOME</entry><entry>The current user's home directory; the default for the <command>cd</command> built-in. The value of this variable is also used by tilde expansion.</entry></row>
<row><entry>IFS</entry><entry>A list of characters that separate fields; used when the shell splits words as part of expansion.</entry></row>
<row><entry>MAIL</entry><entry>If this parameter is set to a file name and the <varname>MAILPATH</varname> variable is not set, Bash informs the user of the arrival of mail in the specified file.</entry></row>
<row><entry>MAILPATH</entry><entry>A colon-separated list of file names which the shell periodically checks for new mail.</entry></row>
<row><entry>OPTARG</entry><entry>The value of the last option argument processed by the <command>getopts</command> built-in.</entry></row>
<row><entry>OPTIND</entry><entry>The index of the last option argument processed by the <command>getopts</command> built-in.</entry></row>
<row><entry>PATH</entry><entry>A colon-separated list of directories in which the shell looks for commands.</entry></row>
<row><entry>PS1</entry><entry>The primary prompt string. The default value is <quote>'\s-\v\$ '</quote>. </entry></row>
<row><entry>PS2</entry><entry>The secondary prompt string. The default value is <quote>'&gt; '</quote>.</entry></row>
</tbody>
</tgroup>
</table>
</sect3>
<sect3 id="sect_03_02_04_02"><title>Bash reserved variables</title>
<para>These variables are set or used by Bash, but other shells do not normally treat them specially.</para>
<table id="table_03_02" frame="all"><title>Reserved Bash variables</title>
<tgroup cols="2" align="left" colsep="1" rowsep="1">
<thead>
<row><entry>Variable name</entry><entry>Definition</entry></row>
</thead>
<tbody>
<row><entry>auto_resume</entry><entry>This variable controls how the shell interacts with the user and job control.</entry></row>
<row><entry>BASH</entry><entry>The full pathname used to execute the current instance of Bash.</entry></row>
<row><entry>BASH_ENV</entry><entry>If this variable is set when Bash is invoked to execute a shell script, its value is expanded and used as the name of a startup file to read before executing the script.</entry></row>
<row><entry>BASH_VERSION</entry><entry>The version number of the current instance of Bash.</entry></row>
<row><entry>BASH_VERSINFO</entry><entry>A read-only array variable whose members hold version information for this instance of Bash.</entry></row>
<row><entry>COLUMNS</entry><entry>Used by the <command>select</command> built-in to determine the terminal width when printing selection lists. Automatically set upon receipt of a <emphasis>SIGWINCH</emphasis> signal.</entry></row>
<row><entry>COMP_CWORD</entry><entry>An index into <varname>${COMP_WORDS}</varname> of the word containing the current cursor position.</entry></row>
<row><entry>COMP_LINE</entry><entry>The current command line.</entry></row>
<row><entry>COMP_POINT</entry><entry>The index of the current cursor position relative to the beginning of the current command.</entry></row>
<row><entry>COMP_WORDS</entry><entry>An array variable consisting of the individual words in the current command line.</entry></row>
<row><entry>COMPREPLY</entry><entry>An array variable from which Bash reads the possible completions generated by a shell function invoked by the programmable completion facility.</entry></row>
<row><entry>DIRSTACK</entry><entry>An array variable containing the current contents of the directory stack.</entry></row>
<row><entry>EUID</entry><entry>The numeric effective user ID of the current user.</entry></row>
<row><entry>FCEDIT</entry><entry>The editor used as a default by the <option>-e</option> option to the <command>fc</command> built-in command.</entry></row>
<row><entry>FIGNORE</entry><entry>A colon-separated list of suffixes to ignore when performing file name completion.</entry></row>
<row><entry>FUNCNAME</entry><entry>The name of any currently-executing shell function.</entry></row>
<row><entry>GLOBIGNORE</entry><entry>A colon-separated list of patterns defining the set of file names to be ignored by file name expansion.</entry></row>
<row><entry>GROUPS</entry><entry>An array variable containing the list of groups of which the current user is a member.</entry></row>
<row><entry>histchars</entry><entry>Up to three characters which control history expansion, quick substitution, and <emphasis>tokenization</emphasis>.</entry></row>
<row><entry>HISTCMD</entry><entry>The history number, or index in the history list, of the current command.</entry></row>
<row><entry>HISTCONTROL</entry><entry>Defines whether a command is added to the history file.</entry></row>
<row><entry>HISTFILE</entry><entry>The name of the file to which the command history is saved. The default value is <filename>~/.bash_history</filename>.</entry></row>
<row><entry>HISTFILESIZE</entry><entry>The maximum number of lines contained in the history file, defaults to 500.</entry></row>
<row><entry>HISTIGNORE</entry><entry>A colon-separated list of patterns used to decide which command lines should be saved in the history list.</entry></row>
<row><entry>HISTSIZE</entry><entry>The maximum number of commands to remember on the history list, default is 500.</entry></row>
<row><entry>HOSTFILE</entry><entry>Contains the name of a file in the same format as <filename>/etc/hosts</filename> that should be read when the shell needs to complete a hostname.</entry></row>
<row><entry>HOSTNAME</entry><entry>The name of the current host.</entry></row>
<row><entry>HOSTTYPE</entry><entry>A string describing the machine Bash is running on.</entry></row>
<row><entry>IGNOREEOF</entry><entry>Controls the action of the shell on receipt of an <emphasis>EOF</emphasis> character as the sole input.</entry></row>
<row><entry>INPUTRC</entry><entry>The name of the Readline initialization file, overriding the default <filename>/etc/inputrc</filename>.</entry></row>
<row><entry>LANG</entry><entry>Used to determine the locale category for any category not specifically selected with a variable starting with <varname>LC_</varname>.</entry></row>
<row><entry>LC_ALL</entry><entry>This variable overrides the value of <varname>LANG</varname> and any other <varname>LC_</varname> variable specifying a locale category.</entry></row>
<row><entry>LC_COLLATE</entry><entry>This variable determines the collation order used when sorting the results of file name expansion, and determines the behavior of range expressions, equivalence classes, and collating sequences within file name expansion and pattern matching.</entry></row>
<row><entry>LC_CTYPE</entry><entry>This variable determines the interpretation of characters and the behavior of character classes within file name expansion and pattern matching.</entry></row>
<row><entry>LC_MESSAGES</entry><entry>This variable determines the locale used to translate double-quoted strings preceded by a <quote>$</quote> sign.</entry></row>
<row><entry>LC_NUMERIC</entry><entry>This variable determines the locale category used for number formatting.</entry></row>
<row><entry>LINENO</entry><entry>The line number in the script or shell function currently executing.</entry></row>
<row><entry>LINES</entry><entry>Used by the <command>select</command> built-in to determine the column length for printing selection lists.</entry></row>
<row><entry>MACHTYPE</entry><entry>A string that fully describes the system type on which Bash is executing, in the standard GNU CPU-COMPANY-SYSTEM format.</entry></row>
<row><entry>MAILCHECK</entry><entry>How often (in seconds) that the shell should check for mail in the files specified in the <varname>MAILPATH</varname> or <varname>MAIL</varname> variables.</entry></row>
<row><entry>OLDPWD</entry><entry>The previous working directory as set by the <command>cd</command> built-in.</entry></row>
<row><entry>OPTERR</entry><entry>If set to the value 1, Bash displays error messages generated by the <command>getopts</command> built-in.</entry></row>
<row><entry>OSTYPE</entry><entry>A string describing the operating system Bash is running on.</entry></row>
<row><entry>PIPESTATUS</entry><entry>An array variable containing a list of exit status values from the processes in the most recently executed foreground pipeline (which may contain only a single command).</entry></row>
<row><entry>POSIXLY_CORRECT</entry><entry>If this variable is in the environment when <command>bash</command> starts, the shell enters POSIX mode.</entry></row>
<row><entry>PPID</entry><entry>The process ID of the shell's parent process.</entry></row>
<row><entry>PROMPT_COMMAND</entry><entry>If set, the value is interpreted as a command to execute before the printing of each primary prompt (<varname>PS1</varname>).</entry></row>
<row><entry>PS3</entry><entry>The value of this variable is used as the prompt for the <command>select</command> command. Defaults to <quote>'#? '</quote></entry></row>
<row><entry>PS4</entry><entry>The value is the prompt printed before the command line is echoed when the <option>-x</option> option is set; defaults to <quote>'+ '</quote>.</entry></row>
<row><entry>PWD</entry><entry>The current working directory as set by the <command>cd</command> built-in command.</entry></row>
<row><entry>RANDOM</entry><entry>Each time this parameter is referenced, a random integer between 0 and 32767 is generated. Assigning a value to this variable seeds the random number generator.</entry></row>
<row><entry>REPLY</entry><entry>The default variable for the <command>read</command> built-in.</entry></row>
<row><entry>SECONDS</entry><entry>This variable expands to the number of seconds since the shell was started.</entry></row>
<row><entry>SHELLOPTS</entry><entry>A colon-separated list of enabled shell options.</entry></row>
<row><entry>SHLVL</entry><entry>Incremented by one each time a new instance of Bash is started.</entry></row>
<row><entry>TIMEFORMAT</entry><entry>The value of this parameter is used as a format string specifying
how the timing information for pipelines prefixed with the <command>time</command> reserved word should be displayed.</entry></row>
<row><entry>TMOUT</entry><entry>If set to a value greater than zero, <varname>TMOUT</varname> is treated as the default timeout for the <command>read</command> built-in. In an interative shell, the value is interpreted as the number of seconds to wait for input after issuing the primary prompt when the shell is interactive. Bash terminates after that number of seconds if input does not arrive.</entry></row>
<row><entry>UID</entry><entry>The numeric, real user ID of the current user.</entry></row>
</tbody>
</tgroup>
</table>
<para>Check the Bash man, info or doc pages for extended information. Some variables are read-only, some are set automatically and some lose their meaning when set to a different value than the default.</para>
</sect3>
</sect2>
<sect2 id="sect_03_02_05"><title>Special parameters</title>
<para>The shell treats several parameters specially. These parameters may only be referenced; assignment to them is not allowed.</para>
<table id="table_03_03" frame="all"><title>Special bash variables</title>
<tgroup cols="2" align="left" colsep="1" rowsep="1">
<thead>
<row><entry>Character</entry><entry>Definition</entry></row>
</thead>
<tbody>
<row><entry><varname>$*</varname></entry><entry>Expands to the positional parameters, starting from one. When the expansion occurs within double quotes, it expands to a single word with the value of each parameter separated by the first character of the <varname>IFS</varname> special variable.</entry></row>
<row><entry><varname>$@</varname></entry><entry>Expands to the positional parameters, starting from one. When the expansion occurs within double quotes, each parameter expands to a separate word.</entry></row>
<row><entry><varname>$#</varname></entry><entry>Expands to the number of positional parameters in decimal.</entry></row>
<row><entry><varname>$?</varname></entry><entry>Expands to the exit status of the most recently executed foreground pipeline.</entry></row>
<row><entry><varname>$-</varname></entry><entry>A hyphen expands to the current option flags as specified upon invocation, by the <command>set</command> built-in command, or those set by the shell itself (such as the <option>-i</option>).</entry></row>
<row><entry><varname>$$</varname></entry><entry>Expands to the process ID of the shell.</entry></row>
<row><entry><varname>$!</varname></entry><entry>Expands to the process ID of the most recently executed background (asynchronous) command.</entry></row>
<row><entry><varname>$0</varname></entry><entry>Expands to the name of the shell or shell script.</entry></row>
<row><entry><varname>$_</varname></entry><entry>The underscore variable is set at shell startup and contains the absolute file name of the shell or script being executed as passed in the argument list. Subsequently, it expands to the last argument to the previous command, after expansion. It is also set to the full pathname of each command executed and placed in the environment exported to that command. When checking mail, this parameter holds the name of the mail file.
</entry></row>
</tbody>
</tgroup>
</table>
<para>The positional parameters are the words following the name of a shell script. They are put into the variables <varname>$1</varname>, <varname>$2</varname>, <varname>$3</varname> and so on. As long as needed, variables are added to an internal array. <varname>$#</varname> holds the total number of parameters, as is demonstrated with this simple script:</para>
<screen>
#!/bin/bash
# positional.sh
# This script reads 3 positional parameters and prints them out.
POSPAR1="$1"
POSPAR2="$2"
POSPAR3="$3"
echo "$1 is the first positional parameter, \$1."
echo "$2 is the second positional parameter, \$2."
echo "$3 is the third positional parameter, \$3."
echo
echo "The total number of positional parameters is $#."
</screen>
<para>Upon execution one could give any numbers of arguments:</para>
<screen>
<prompt>franky ~&gt;</prompt> <command>positional.sh <parameter>one two three four five</parameter></command>
one is the first positional parameter, $1.
two is the second positional parameter, $2.
three is the third positional parameter, $3.
The total number of positional parameters is 5.
<prompt>franky ~&gt;</prompt> <command>positional.sh <parameter>one two</parameter></command>
one is the first positional parameter, $1.
two is the second positional parameter, $2.
is the third positional parameter, $3.
The total number of positional parameters is 2.
</screen>
<para>More on evaluating these parameters is in <xref linkend="chap_07" /> and <xref linkend="sect_09_07" />.</para>
<para>Some examples on the other special parameters:</para>
<screen>
<prompt>franky ~&gt;</prompt> <command>grep <parameter>dictionary</parameter> <filename>/usr/share/dict/words</filename></command>
dictionary
<prompt>franky ~&gt;</prompt> <command>echo <varname>$_</varname></command>
/usr/share/dict/words
<prompt>franky ~&gt;</prompt> <command>echo <varname>$$</varname></command>
10662
<prompt>franky ~&gt;</prompt> <command>mozilla &amp;</command>
[1] 11064
<prompt>franky ~&gt;</prompt> <command>echo <varname>$!</varname></command>
11064
<prompt>franky ~&gt;</prompt> <command>echo <varname>$0</varname></command>
bash
<prompt>franky ~&gt;</prompt> <command>ls <filename>doesnotexist</filename></command>
ls: doesnotexist: No such file or directory
<prompt>franky ~&gt;</prompt> <command>echo <varname>$?</varname></command>
0
<prompt>franky ~&gt;</prompt> <command>echo <varname>$?</varname></command>
1
<prompt>franky ~&gt;</prompt>
</screen>
<para>User <emphasis>franky</emphasis> starts entering the <command>id</command>command, which results in the assignment of the <varname>_</varname> variable. The process ID of his shell is 10662. After putting a job in the background, the <varname>!</varname> holds the process ID of the backgrounded job. The shell running is <command>bash</command>. When a mistake is made, <varname>?</varname> holds an exit code different from 0 (zero).</para>
</sect2>
<sect2 id="sect_03_02_06"><title>Script recycling with variables</title>
<para>Apart from making the script more readable, variables will also enable you to faster apply a script in another environment or for another purpose. Consider the following example, a very simple script that makes a backup of <emphasis>franky</emphasis>'s home directory to a remote server:</para>
<screen>
#!/bin/bash
# This script makes a backup of my home directory.
cd /home
# This creates the archive
tar cf /var/tmp/home_franky.tar franky &gt; /dev/null 2&gt;&amp;1
# First remove the old bzip2 file. Redirect errors because this generates some if the archive
# does not exist. Then create a new compressed file.
rm /var/tmp/home_franky.tar.bz2 2&gt; /dev/null
bzip2 /var/tmp/home_franky.tar
# Copy the file to another host - we have ssh keys for making this work without intervention.
scp /var/tmp/home_franky.tar.bz2 bordeaux:/opt/backup/franky &gt; /dev/null 2&gt;&amp;1
# Create a timestamp in a logfile.
date &gt; /home/franky/log/home_backup.log
echo backup succeeded &gt; /home/franky/log/home_backup.log
</screen>
<para>First of all, you are more likely to make errors if you name files and directories manually each time you need them. Secondly, suppose <emphasis>franky</emphasis> wants to give this script to <emphasis>carol</emphasis>, then carol will have to do quite some editing before she can use the script to back up her home directory. The same is true if <emphasis>franky</emphasis> wants to use this script for backing up other directories. For easy recycling, make all files, directories, usernames, servernames etcetera variable. Thus, you only need to edit a value once, without having to go through the entire script to check where a parameter occurs. This is an example:</para>
<screen>
#!/bin/bash
# This script makes a backup of my home directory.
# Change the values of the variables to make the script work for you:
BACKUPDIR=/home
BACKUPFILES=franky
TARFILE=/var/tmp/home_franky.tar
BZIPFILE=/var/tmp/home_franky.tar.bz2
SERVER=bordeaux
REMOTEDIR=/opt/backup/franky
LOGFILE=/home/franky/log/home_backup.log
cd $BACKUPDIR
# This creates the archive
tar cf $TARFILE $BACKUPFILES &gt; /dev/null 2&gt;&amp;1
# First remove the old bzip2 file. Redirect errors because this generates some if the archive
# does not exist. Then create a new compressed file.
rm $BZIPFILE 2&gt; /dev/null
bzip2 $TARFILE
# Copy the file to another host - we have ssh keys for making this work without intervention.
scp $BZIPFILE $SERVER:$REMOTEDIR &gt; /dev/null 2&gt;&amp;1
# Create a timestamp in a logfile.
date &gt; $LOGFILE
echo backup succeeded &gt; $LOGFILE
</screen>
<note><title>Large directories and low bandwidth</title>
<para>The above is purely an example that everybody can understand, using a small directory and a host on the same subnet. Depending on your bandwidth, the size of the directory and the location of the remote server, it can take an awful lot of time to make backups using this mechanism. For larger directories and lower bandwidth, use <command>rsync</command> to keep the directories at both ends synchronized.</para>
</note>
</sect2>
</sect1>
<sect1 id="sect_03_03"><title>Quoting characters</title>
<sect2 id="sect_03_03_01"><title>Why?</title>
<para>A lot of keys have special meanings in some context or other. Quoting is used to remove the special meaning of characters or words: quotes can disable special treatment for special characters, they can prevent reserved words from being recognized as such and they can disable parameter expansion.</para>
</sect2>
<sect2 id="sect_03_03_02"><title>Escape characters</title>
<para>Escape characters are used to remove the special meaning from a single character. A non-quoted backslash, \, is used as an escape character in Bash. It preserves the literal value of the next character that follows, with the exception of <emphasis>newline</emphasis>. If a newline character appears immediately after the backslash, it marks the continuation of a line when it is longer that the width of the terminal; the backslash is removed from the input stream and effectively ignored.</para>
<screen>
<prompt>franky ~&gt;</prompt> <command><varname>date</varname>=<parameter>20021226</parameter></command>
<prompt>franky ~&gt;</prompt> <command>echo <varname>$date</varname></command>
20021226
<prompt>franky ~&gt;</prompt> <command>echo <parameter>\$date</parameter></command>
$date
</screen>
<para>In this example, the variable <varname>date</varname> is created and set to hold a value. The first <command>echo</command> displays the value of the variable, but for the second, the dollar sign is escaped.</para>
</sect2>
<sect2 id="sect_03_03_03"><title>Single quotes</title>
<para>Single quotes ('') are used to preserve the literal value of each character enclosed within the quotes. A single quote may not occur between single quotes, even when preceded by a backslash.</para>
<para>We continue with the previous example:</para>
<screen>
<prompt>franky ~&gt;</prompt> <command>echo <parameter>'$date'</parameter></command>
$date
</screen>
</sect2>
<sect2 id="sect_03_03_04"><title>Double quotes</title>
<para>Using double quotes the literal value of all characters enclosed is preserved, except for the dollar sign, the backticks (backward single quotes, ``) and the backslash.</para>
<para>The dollar sign and the backticks retain their special meaning within the double quotes.</para>
<para>The backslash retains its meaning only when followed by dollar, backtick, double quote, backslash or newline. Within double quotes, the backslashes are removed from the input stream when followed by one of these characters. Backslashes preceding characters that don't have a special meaning are left unmodified for processing by the shell interpreter.</para>
<para>A double quote may be quoted within double quotes by preceding it with a backslash.</para>
<screen>
<prompt>franky ~&gt;</prompt> <command>echo <varname>"$date"</varname></command>
20021226
<prompt>franky ~&gt;</prompt> <command>echo <parameter>"`date`"</parameter></command>
Sun Apr 20 11:22:06 CEST 2003
<prompt>franky ~&gt;</prompt> <command>echo <parameter>"I'd say: \"Go for it!\""</parameter></command>
I'd say: "Go for it!"
<prompt>franky ~&gt;</prompt> <command>echo <parameter>"\"</parameter></command>
<prompt>More input&gt;</prompt><command>"</command>
<prompt>franky ~&gt;</prompt> <command>echo <parameter>"\\"</parameter></command>
\
</screen>
</sect2>
<sect2 id="sect_03_03_05"><title>ANSI-C quoting</title>
<para>Words in the form <quote>$'STRING'</quote> are treated in a special way. The word expands to a string, with backslash-escaped characters replaced as specified by the ANSI-C standard. Backslash escape sequences can be found in the Bash documentation.</para>
</sect2>
<sect2 id="sect_03_03_06"><title>Locales</title>
<para>A double-quoted string preceded by a dollar sign will cause the string to be translated according to the current locale. If the current locale is <quote>C</quote> or <quote>POSIX</quote>, the dollar sign is ignored. If the string is translated and replaced, the replacement is double-quoted.</para>
</sect2>
</sect1>
<sect1 id="sect_03_04"><title>Shell expansion</title>
<sect2 id="sect_03_04_0"><title>General</title>
<para>After the command has been split into <emphasis>tokens</emphasis> (see <xref linkend="sect_01_04_01_01" />), these tokens or words are expanded or resolved. There are eight kinds of expansion performed, which we will discuss in the next sections, in the order that they are expanded.</para>
<para>After all expansions, quote removal is performed.</para>
</sect2>
<sect2 id="sect_03_04_01"><title>Brace expansion</title>
<para>Brace expansion is a mechanism by which arbitrary strings may be generated. Patterns to be brace-expanded take the form of an optional <emphasis>PREAMBLE</emphasis>, followed by a series of comma-separated strings between a pair of braces, followed by an optional <emphasis>POSTSCRIPT</emphasis>. The preamble is prefixed to each string contained within the braces, and the postscript is then appended to each resulting string, expanding left to right.</para>
<para>Brace expansions may be nested. The results of each expanded string are not sorted; left to right order is preserved:</para>
<screen>
<prompt>franky ~&gt;</prompt> <command>echo <parameter>sp{el,il,al}l</parameter></command>
spell spill spall
</screen>
<para>Brace expansion is performed before any other expansions, and any characters special to other expansions are preserved in the result. It is strictly textual. Bash does not apply any syntactic interpretation to the context of the expansion or the text between the braces. To avoid conflicts with parameter expansion, the string <quote>${</quote> is not considered eligible for brace expansion.</para>
<para>A correctly-formed brace expansion must contain unquoted opening and closing braces, and at least one unquoted comma. Any incorrectly formed brace expansion is left unchanged.</para>
</sect2>
<sect2 id="sect_03_04_02"><title>Tilde expansion</title>
<para>If a word begins with an unquoted tilde character (<quote>~</quote>), all of the characters up to the first unquoted slash (or all characters, if there is no unquoted slash) are considered a <emphasis>tilde-prefix</emphasis>. If none of the characters in the tilde-prefix are quoted, the characters in the tilde-prefix following the tilde are treated as a possible login name. If this login name is the null string, the tilde is replaced with the value of the <varname>HOME</varname> shell variable. If <varname>HOME</varname> is unset, the home directory of the user executing the shell is substituted instead. Otherwise, the tilde-prefix is replaced with the home directory associated with the specified login name.</para>
<para>If the tilde-prefix is <quote>~+</quote>, the value of the shell variable <varname>PWD</varname> replaces the tilde-prefix. If the tilde-prefix is <quote>~-</quote>, the value of the shell variable <varname>OLDPWD</varname>, if it is set, is substituted.</para>
<para>If the characters following the tilde in the tilde-prefix consist of a number N, optionally prefixed by a <quote>+</quote> or a <quote>-</quote>, the tilde-prefix is replaced with the corresponding element from the directory stack, as it would be displayed by the <command>dirs</command> built-in invoked with the characters following tilde in the tilde-prefix as an argument. If the tilde-prefix, without the tilde, consists of a number without a leading <quote>+</quote> or <quote>-</quote>, <quote>+</quote> is assumed.</para>
<para>If the login name is invalid, or the tilde expansion fails, the word
is left unchanged.</para>
<para>Each variable assignment is checked for unquoted tilde-prefixes immediately following a <quote>:</quote> or <quote>=</quote>. In these cases, tilde expansion is also performed. Consequently, one may use file names with tildes in assignments to <varname>PATH</varname>, <varname>MAILPATH</varname>, and <varname>CDPATH</varname>, and the shell assigns the expanded value.</para>
<para>Example:</para>
<screen>
<prompt>franky ~&gt;</prompt> <command>export <varname>PATH</varname>=<parameter>"$PATH:~/testdir"</parameter></command>
</screen>
<para><filename>~/testdir</filename> will be expanded to <varname>$HOME</varname><filename>/testdir</filename>, so if <varname>$HOME</varname> is <filename>/var/home/franky</filename>, the directory <filename>/var/home/franky/testdir</filename> will be added to the content of the <varname>PATH</varname> variable.</para>
</sect2>
<sect2 id="sect_03_04_03"><title>Shell parameter and variable expansion</title>
<para>The <quote>$</quote> character introduces parameter expansion, command substitution, or arithmetic expansion. The parameter name or symbol to be expanded may be enclosed in braces, which are optional but serve to protect the variable to be expanded from characters immediately following it which could be interpreted as part of the name.</para>
<para>When braces are used, the matching ending brace is the first <quote>}</quote> not escaped by a backslash or within a quoted string, and not within an embedded arithmetic expansion, command substitution, or parameter expansion.</para>
<para>The basic form of parameter expansion is <quote>${PARAMETER}</quote>. The value of <quote>PARAMETER</quote> is substituted. The braces are required when <quote>PARAMETER</quote> is a positional parameter with more than one digit, or when <quote>PARAMETER</quote> is followed by a character that is not to be interpreted as part of its name.</para>
<para>If the first character of <quote>PARAMETER</quote> is an exclamation point, Bash uses the value of the variable formed from the rest of <quote>PARAMETER</quote> as the name of the variable; this variable is then expanded and that value is used in the rest of the substitution, rather than the value of <quote>PARAMETER</quote> itself. This is known as <emphasis>indirect expansion</emphasis>.</para>
<para>You are certainly familiar with straight parameter expansion, since it happens all the time, even in the simplest of cases, such as the one above or the following:</para>
<screen>
<prompt>franky ~&gt;</prompt> <command>echo <varname>$SHELL</varname></command>
/bin/bash
</screen>
<para>The following is an example of indirect expansion:</para>
<screen>
<prompt>franky ~&gt;</prompt> <command>echo <varname>${!N*}</varname></command>
NNTPPORT NNTPSERVER NPX_PLUGIN_PATH
</screen>
<para>Note that this is not the same as <command>echo <varname>$N*</varname></command>.</para>
<para>The following construct allows for creation of the named variable if it does not yet exist:</para>
<cmdsynopsis><command>${<varname>VAR</varname>:=<parameter>value</parameter>}</command></cmdsynopsis>
<para>Example:</para>
<screen>
<prompt>franky ~&gt;</prompt> <command>echo <varname>$FRANKY</varname></command>
<prompt>franky ~&gt;</prompt> <command>echo <parameter>${FRANKY:=Franky}</parameter></command>
Franky
</screen>
<para>Special parameters, among others the positional parameters, may not be assigned this way, however.</para>
<para>We will further discuss the use of the curly braces for treatment of variables in <xref linkend="chap_10" />. More information can also be found in the Bash info pages.</para>
</sect2>
<sect2 id="sect_03_04_04"><title>Command substitution</title>
<para>Command substitution allows the output of a command to replace the
command itself. Command substitution occurs when a command is enclosed like this:</para>
<cmdsynopsis><command>$(command)</command></cmdsynopsis>
<para>or like this using backticks:</para>
<cmdsynopsis><command>`command`</command></cmdsynopsis>
<para>Bash performs the expansion by executing COMMAND and replacing the command substitution with the standard output of the command, with any trailing newlines deleted. Embedded newlines are not deleted, but they may be removed during word splitting.</para>
<screen>
<prompt>franky ~&gt;</prompt> <command>echo <parameter>`date`</parameter></command>
Thu Feb 6 10:06:20 CET 2003
</screen>
<para>When the old-style backquoted form of substitution is used, backslash retains its literal meaning except when followed by <quote>$</quote>, <quote>`</quote>, or <quote>\</quote>. The first backticks not preceded by a backslash terminates the command substitution. When using the <quote>$(COMMAND)</quote> form, all characters between the parentheses make up the command; none are treated specially.</para>
<para>Command substitutions may be nested. To nest when using the backquoted form, escape the inner backticks with backslashes.</para>
<para>If the substitution appears within double quotes, word splitting and file name expansion are not performed on the results.</para>
</sect2>
<sect2 id="sect_03_04_05"><title>Arithmetic expansion</title>
<para>Arithmetic expansion allows the evaluation of an arithmetic expression and the substitution of the result. The format for arithmetic expansion is:</para><cmdsynopsis><command>$(( EXPRESSION ))</command></cmdsynopsis>
<para>The expression is treated as if it were within double quotes, but a double quote inside the parentheses is not treated specially. All tokens in the expression undergo parameter expansion, command substitution, and quote removal. Arithmetic substitutions may be nested.</para>
<para>Evaluation of arithmetic expressions is done in fixed-width integers with no check for overflow - although division by zero is trapped and recognized as an error. The operators are roughly the same as in the C programming language. In order of decreasing precedence, the list looks like this:</para>
<table id="table_03_04" frame="all">
<title>Arithmetic operators</title>
<tgroup cols="2" align="left" colsep="1" rowsep="1">
<thead>
<row><entry>Operator</entry><entry>Meaning</entry></row>
</thead>
<tbody>
<row><entry>VAR++ and VAR--</entry><entry>variable post-increment and post-decrement</entry></row>
<row><entry>++VAR and --VAR</entry><entry>variable pre-increment and pre-decrement</entry></row>
<row><entry>- and +</entry><entry>unary minus and plus</entry></row>
<row><entry>! and ~</entry><entry>logical and bitwise negation</entry></row>
<row><entry>**</entry><entry>exponentiation</entry></row>
<row><entry>*, / and %</entry><entry>multiplication, division, remainder</entry></row>
<row><entry>+ and -</entry><entry>addition, subtraction</entry></row>
<row><entry>&lt;&lt; and &gt;&gt;</entry><entry>left and right bitwise shifts</entry></row>
<row><entry>&lt;=, &gt;=, &lt; and &gt;</entry><entry>comparison operators</entry></row>
<row><entry>== and !=</entry><entry>equality and inequality</entry></row>
<row><entry>&amp;</entry><entry>bitwise AND</entry></row>
<row><entry>^</entry><entry>bitwise exclusive OR</entry></row>
<row><entry>|</entry><entry>bitwise OR</entry></row>
<row><entry>&amp;&amp;</entry><entry>logical AND</entry></row>
<row><entry>||</entry><entry>logical OR</entry></row>
<row><entry>expr ? expr : expr</entry><entry>conditional evaluation</entry></row>
<row><entry>=, *=, /=, %=, +=, -=, &lt;&lt;=, &gt;&gt;=, &amp;=, ^= and |=</entry><entry>assignments</entry></row>
<row><entry>,</entry><entry>separator between expressions</entry></row>
</tbody>
</tgroup>
</table>
<para>Shell variables are allowed as operands; parameter expansion is performed before the expression is evaluated. Within an expression, shell variables may also be referenced by name without using the parameter expansion syntax. The value of a variable is evaluated as an arithmetic expression when it is referenced. A shell variable need not have its integer attribute turned on to be used in an expression.</para>
<para>Constants with a leading 0 (zero) are interpreted as octal numbers. A
leading <quote>0x</quote> or <quote>0X</quote> denotes hexadecimal. Otherwise, numbers take the form <quote>[BASE'#']N</quote>, where <quote>BASE</quote> is a decimal number between 2 and 64 representing the arithmetic base, and N is a number in that base. If <quote>BASE'#'</quote> is omitted, then base 10 is used. The digits greater than 9 are represented by the lowercase letters, the uppercase letters, <quote>@</quote>, and <quote>_</quote>, in that order. If <quote>BASE</quote> is less than or equal to 36, lowercase and uppercase letters may be used interchangably to represent numbers between 10 and 35.</para>
<para>Operators are evaluated in order of precedence. Sub-expressions in parentheses are evaluated first and may override the precedence rules above.</para>
<para>Wherever possible, Bash users should try to use the syntax with angular brackets:</para>
<cmdsynopsis><command>$[ EXPRESSION ]</command></cmdsynopsis>
<para>However, this will only calculate the result of <emphasis>EXPRESSION</emphasis>, and do no tests:</para>
<screen>
<prompt>franky ~&gt;</prompt> <command>echo <parameter>$[365*24]</parameter></command>
8760
</screen>
<para>See <xref linkend="sect_07_01_02_02" />, among others, for practical examples in scripts.</para>
</sect2>
<sect2 id="sect_03_04_06"><title>Process substitution</title>
<para>Process substitution is supported on systems that support named pipes (FIFOs) or the <filename>/dev/fd</filename> method of naming open files. It takes the form of</para>
<cmdsynopsis><command>&lt;(LIST)</command></cmdsynopsis>
<para>or</para>
<cmdsynopsis><command>&gt;(LIST)</command></cmdsynopsis>
<para>The process <varname>LIST</varname> is run with its input or output connected to a FIFO or some file in <filename>/dev/fd</filename>. The name of this file is passed as an argument to the current command as the result of the expansion. If the <quote>&gt;(LIST)</quote> form is used, writing to the file will provide input for <varname>LIST</varname>. If the <quote>&lt;(LIST)</quote> form is used, the file passed as an argument should be read to obtain the output of <varname>LIST</varname>. Note that no space may appear between the &lt; or &gt; signs and the left parenthesis, otherwise the construct would be interpreted as a redirection.</para>
<para>When available, process substitution is performed simultaneously with parameter and variable expansion, command substitution, and arithmetic expansion.</para>
<para>More information in <xref linkend="sect_08_02_03" />.</para>
</sect2>
<sect2 id="sect_03_04_07"><title>Word splitting</title>
<para>The shell scans the results of parameter expansion, command substitution, and arithmetic expansion that did not occur within double quotes for word splitting.</para>
<para>The shell treats each character of <varname>$IFS</varname> as a delimiter, and splits the results of the other expansions into words on these characters. If <varname>IFS</varname> is unset, or its value is exactly <quote>'&lt;space&gt;&lt;tab&gt;&lt;newline&gt;'</quote>, the default, then any sequence of <varname>IFS</varname> characters serves to delimit words. If <varname>IFS</varname> has a value other than the default, then sequences of the whitespace characters <quote>space</quote> and <quote>Tab</quote> are ignored at the beginning and end of the word, as long as the whitespace character is in the value of <varname>IFS</varname> (an <varname>IFS</varname> whitespace character). Any character in <varname>IFS</varname> that is not <varname>IFS</varname> whitespace, along with any adjacent <varname>IF</varname> whitespace characters, delimits a field. A sequence of <varname>IFS</varname> whitespace characters is also treated as a delimiter. If the value of <varname>IFS</varname> is null, no word splitting occurs.</para>
<para>Explicit null arguments (<quote>""</quote> or <quote>''</quote>) are retained. Unquoted implicit null arguments, resulting from the expansion of parameters that have no values, are removed. If a parameter with no value is expanded within double quotes, a null argument results and is retained.</para>
<note><title>Expansion and word splitting</title><para>If no expansion occurs, no splitting is performed.</para></note>
</sect2>
<sect2 id="sect_03_04_08"><title>File name expansion</title>
<para>After word splitting, unless the <option>-f</option> option has been set (see <xref linkend="sect_02_03_02" />), Bash scans each word for the characters <quote>*</quote>, <quote>?</quote>, and <quote>[</quote>. If one of these characters appears, then the word is regarded as a <emphasis>PATTERN</emphasis>, and replaced with an alphabetically sorted list of file names matching the pattern. If no matching file names are found, and the shell option <option>nullglob</option> is disabled, the word is left unchanged. If the <option>nullglob</option> option is set, and no matches are found, the word is removed. If the shell option <option>nocaseglob</option> is enabled, the match is performed without regard to the case of alphabetic characters.</para>
<para>When a pattern is used for file name generation, the character <quote>.</quote> at the start of a file name or immediately following a slash must be matched explicitly, unless the shell option <option>dotglob</option> is set. When matching a file name, the slash character must always be matched explicitly. In other cases, the <quote>.</quote> character is not treated specially.</para>
<para>The <varname>GLOBIGNORE</varname> shell variable may be used to restrict the set of file names matching a pattern. If <varname>GLOBIGNORE</varname> is set, each matching file name that also matches one of the patterns in <varname>GLOBIGNORE</varname> is removed from the list of matches. The file names <filename>.</filename> and <filename>..</filename> are always ignored, even when <varname>GLOBIGNORE</varname> is set. However, setting <varname>GLOBIGNORE</varname> has the effect of enabling the <option>dotglob</option> shell option, so all other file names beginning with a <quote>.</quote> will match. To get the old behavior of ignoring file names beginning with a <quote>.</quote>, make <quote>.*</quote> one of the patterns in <varname>GLOBIGNORE</varname>. The <option>dotglob</option> option is disabled when <varname>GLOBIGNORE</varname> is unset.</para>
</sect2>
</sect1>
<sect1 id="sect_03_05"><title>Aliases</title>
<sect2 id="sect_03_05_01"><title>What are aliases?</title>
<para>An alias allows a string to be substituted for a word when it is used as the first word of a simple command. The shell maintains a list of aliases that may be set and unset with the <command>alias</command> and <command>unalias</command> built-in commands. Issue the <command>alias</command> without options to display a list of aliases known to the current shell.</para>
<screen>
<prompt>franky: ~&gt;</prompt> <command>alias</command>
alias ..='cd ..'
alias ...='cd ../..'
alias ....='cd ../../..'
alias PAGER='less -r'
alias Txterm='export TERM=xterm'
alias XARGS='xargs -r'
alias cdrecord='cdrecord -dev 0,0,0 -speed=8'
alias e='vi'
alias egrep='grep -E'
alias ewformat='fdformat -n /dev/fd0u1743; ewfsck'
alias fgrep='grep -F'
alias ftp='ncftp -d15'
alias h='history 10'
alias fformat='fdformat /dev/fd0H1440'
alias j='jobs -l'
alias ksane='setterm -reset'
alias ls='ls -F --color=auto'
alias m='less'
alias md='mkdir'
alias od='od -Ax -ta -txC'
alias p='pstree -p'
alias ping='ping -vc1'
alias sb='ssh blubber'
alias sl='ls'
alias ss='ssh octarine'
alias sss='ssh -C server1.us.xalasys.com'
alias sssu='ssh -C -l root server1.us.xalasys.com'
alias tar='gtar'
alias tmp='cd /tmp'
alias unaliasall='unalias -a'
alias vi='eval `resize`;vi'
alias vt100='export TERM=vt100'
alias which='type'
alias xt='xterm -bg black -fg white &amp;'
<prompt>franky ~&gt;</prompt>
</screen>
<para>Aliases are useful for specifying the default version of a command that exists in several versions on your system, or to specify default options to a command. Another use for aliases is for correcting incorrect spelling.</para>
<para>The first word of each simple command, if unquoted, is checked to see if it has an alias. If so, that word is replaced by the text of the alias. The alias name and the replacement text may contain any valid shell input, including shell metacharacters, with the exception that the alias name may not contain <quote>=</quote>. The first word of the replacement text is tested for aliases, but a word that is identical to an alias being expanded is not expanded a second time. This means that one may alias <command>ls</command> to <command>ls <option>-F</option></command>, for instance, and Bash will not try to recursively expand the replacement text. If the last character of the alias value is a space or tab character, then the next command word following the alias is also checked for alias expansion.</para>
<para>Aliases are not expanded when the shell is not interactive, unless
the <option>expand_aliases</option> option is set using the <command>shopt</command> shell built-in.</para>
</sect2>
<sect2 id="sect_03_05_02"><title>Creating and removing aliases</title>
<para>Aliases are created using the <command>alias</command> shell built-in. For permanent use, enter the <command>alias</command> in one of your shell initialization files; if you just enter the alias on the command line, it is only recognized within the current shell.</para>
<screen>
<prompt>franky ~&gt;</prompt> <command>alias <parameter>dh='df -h'</parameter></command>
<prompt>franky ~&gt;</prompt> <command>dh</command>
Filesystem Size Used Avail Use% Mounted on
/dev/hda7 1.3G 272M 1018M 22% /
/dev/hda1 121M 9.4M 105M 9% /boot
/dev/hda2 13G 8.7G 3.7G 70% /home
/dev/hda3 13G 5.3G 7.1G 43% /opt
none 243M 0 243M 0% /dev/shm
/dev/hda6 3.9G 3.2G 572M 85% /usr
/dev/hda5 5.2G 4.3G 725M 86% /var
<prompt>franky ~&gt;</prompt> <command>unalias <parameter>dh</parameter></command>
<prompt>franky ~&gt;</prompt> <command>dh</command>
bash: dh: command not found
<prompt>franky ~&gt;</prompt>
</screen>
<para>Bash always reads at least one complete line of input before executing any of the commands on that line. Aliases are expanded when a command is read, not when it is executed. Therefore, an alias definition appearing on the same line as another command does not take effect until the next line of input is read. The commands following the alias definition on that line are not affected by the new alias. This behavior is also an issue when functions are executed. Aliases are expanded when a function definition is read, not when the function is executed, because a function definition is itself a compound command. As a consequence, aliases defined in a function are not available until after that function is executed. To be safe, always put alias definitions on a separate line, and do not use <command>alias</command> in compound commands.</para>
<para>Aliases are not inherited by child processes. Bourne shell (<command>sh</command>) does not recognize aliases.</para>
<para>More about functions is in <xref linkend="chap_11" />.</para>
<tip><title>Functions are faster</title><para>Aliases are looked up after functions and thus resolving is slower. While aliases are easier to understand, shell functions are preferred over aliases for almost every purpose.</para></tip>
</sect2>
</sect1>
<sect1 id="sect_03_06"><title>More Bash options</title>
<sect2 id="sect_03_06_01"><title>Displaying options</title>
<para>We already discussed a couple of Bash options that are useful for debugging your scripts. In this section, we will take a more in-depth view of the Bash options.</para>
<para>Use the <option>-o</option> option to <command>set</command> to display all shell options:</para>
<screen>
<prompt>willy:~&gt;</prompt> <command>set <option>-o</option></command>
allexport off
braceexpand on
emacs on
errexit off
hashall on
histexpand on
history on
ignoreeof off
interactive-comments on
keyword off
monitor on
noclobber off
noexec off
noglob off
nolog off
notify off
nounset off
onecmd off
physical off
posix off
privileged off
verbose off
vi off
xtrace off
</screen>
<para>See the Bash Info pages, section <menuchoice><guimenu>Shell Built-in Commands</guimenu><guimenuitem>The Set Built-in</guimenuitem></menuchoice> for a description of each option. A lot of options have one-character shorthands: the <option>xtrace</option> option, for instance, is equal to specifying <command>set <option>-x</option></command>.</para>
</sect2>
<sect2 id="sect_03_06_02"><title>Changing options</title>
<para>Shell options can either be set different from the default upon calling the shell, or be set during shell operation. They may also be included in the shell resource configuration files.</para>
<para>The following command executes a script in POSIX-compatible mode:</para>
<screen>
<prompt>willy:~/scripts&gt;</prompt> <command>bash <option>--posix</option> <filename>script.sh</filename></command>
</screen>
<para>For changing the current environment temporarily, or for use in a script,
we would rather use <command>set</command>. Use <emphasis>-</emphasis> (dash) for enabling an option, <emphasis>+</emphasis> for disabling:</para>
<screen>
<prompt>willy:~/test&gt;</prompt> <command>set <option>-o</option> <parameter>noclobber</parameter></command>
<prompt>willy:~/test&gt;</prompt> <command>touch <filename>test</filename></command>
<prompt>willy:~/test&gt;</prompt> <command>date &gt; <filename>test</filename></command>
bash: test: cannot overwrite existing file
<prompt>willy:~/test&gt;</prompt> <command>set <option>+o</option> <parameter>noclobber</parameter></command>
<prompt>willy:~/test&gt;</prompt> <command>date &gt; <filename>test</filename></command>
</screen>
<para>The above example demonstrates the <option>noclobber</option> option, which prevents existing files from being overwritten by redirection operations. The same goes for one-character options, for instance <option>-u</option>, which will treat unset variables as an error when set, and exits a non-interactive shell upon
encountering such errors:</para>
<screen>
<prompt>willy:~&gt;</prompt> <command>echo <varname>$VAR</varname></command>
<prompt>willy:~&gt;</prompt> <command>set <option>-u</option></command>
<prompt>willy:~&gt;</prompt> <command>echo <varname>$VAR</varname></command>
bash: VAR: unbound variable
</screen>
<para>This option is also useful for detecting incorrect content assignment to variables: the same error will also occur, for instance, when assigning a character string to a variable that was declared explicitly as one holding only integer values.</para>
<para>One last example follows, demonstrating the <option>noglob</option> option, which prevents special characters from being expanded:</para>
<screen>
<prompt>willy:~/testdir&gt;</prompt> <command>set <option>-o</option> <parameter>noglob</parameter></command>
<prompt>willy:~/testdir&gt;</prompt> <command>touch <filename>*</filename></command>
<prompt>willy:~/testdir&gt;</prompt> <command>ls <option>-l</option> <filename>*</filename></command>
-rw-rw-r-- 1 willy willy 0 Feb 27 13:37 *
</screen>
</sect2>
</sect1>
<sect1 id="sect_03_07"><title>Summary</title>
<para>The Bash environment can be configured globally and on a per user basis. Various configuration files are used to fine-tune the behavior of the shell.</para>
<para>These files contain shell options, settings for variables, function definitions and various other building blocks for creating ourselves a cosy environment.</para>
<para>Except for the reserved Bourne shell, Bash and special parameters, variable names can be chosen more or less freely.</para>
<para>Because a lot of characters have double or even triple meanings, depending on the environment, Bash uses a system of quoting to take away special meaning from one or multiple characters when special treatment is not wanted.</para>
<para>Bash uses various methods of expanding command line entries in order to determine which commands to execute.</para>
</sect1>
<sect1 id="sect_03_08"><title>Exercises</title>
<para>For this exercise, you will need to read the <command>useradd</command> man pages, because we are going to use the <filename>/etc/skel</filename> directory to hold default shell configuration files, which are copied to the home directory of each newly added user.</para>
<para>First we will do some general exercises on setting and displaying variables.</para>
<orderedlist>
<listitem><para>Create 3 variables, <varname>VAR1</varname>, <varname>VAR2</varname> and <varname>VAR3</varname>; initialize them to hold the values <quote>thirteen</quote>, <quote>13</quote> and <quote>Happy Birthday</quote> respectively.</para></listitem>
<listitem><para>Display the values of all three variables.</para></listitem>
<listitem><para>Are these local or global variables?</para></listitem>
<listitem><para>Remove <varname>VAR3</varname>.</para></listitem>
<listitem><para>Can you see the two remaining variables in a new terminal window?</para></listitem>
<listitem><para>Edit <filename>/etc/profile</filename> so that all users are greeted upon login (test this).</para></listitem>
<listitem><para>For the <emphasis>root</emphasis> account, set the prompt to something like <quote>Danger!! root is doing stuff in \w</quote>, preferably in a bright color such as red or pink or in reverse video mode.</para></listitem>
<listitem><para>Make sure that newly created users also get a nice personalized prompt which informs them on which system in which directory they are working. Test your changes by adding a new user and logging in as that user.</para></listitem>
<listitem><para>Write a script in which you assign two integer values to two variables. The script should calculate the surface of a rectangle which has these proportions. It should be aired with comments and generate elegant output.</para></listitem>
</orderedlist>
<para>Don't forget to <command>chmod</command> your scripts!</para>
</sect1>
</chapter>