We already mentioned a couple of environment variables, such as PATH and HOME. Until now, we only saw examples in which they serve a certain purpose to the shell. But there are many other Linux utilities that need information about you in order to do a good job.
What other information do programs need apart from paths and home directories?
A lot of programs want to know about the kind of terminal you are using; this information is stored in the TERM variable. In text mode, this will be the linux terminal emulation, in graphical mode you are likely to use xterm. Lots of programs want to know what your favorite editor is, in case they have to start an editor in a subprocess. The shell you are using is stored in the SHELL variable, the operating system type in OS and so on. A list of all variables currently defined for your session can be viewed entering the printenv command.
The environment variables are managed by the shell. As opposed to regular shell variables, environment variables are inherited by any program you start, including another shell. New processes are assigned a copy of these variables, which they can read, modify and pass on in turn to their own child processes.
There is nothing special about variable names, except that the common ones are in upper case characters by convention. You may come up with any name you want, although there are standard variables that are important enough to be the same on every Linux system, such as PATH and HOME.
The following table gives an overview of the most common predefined variables:
Table 7-1. Common environment variables
| Variable name | Stored information |
|---|---|
| DISPLAY | used by the X Window system to identify the display server |
| DOMAIN | domain name |
| EDITOR | stores your favorite line editor |
| HISTSIZE | size of the shell history file in number of lines |
| HOME | path to your home directory |
| HOSTNAME | local host name |
| INPUTRC | location of definition file for input devices such as keyboard |
| LANG | preferred language |
| LD_LIBRARY_PATH | paths to search for libraries |
| LOGNAME | login name |
| location of your incoming mail folder | |
| MANPATH | paths to search for man pages |
| OS | string describing the operating system |
| OSTYPE | more information about version etc. |
| PAGER | used by programs like man which need to know what to do in case output is more than one terminal window. |
| PATH | serach paths for commands |
| PS1 | primary prompt |
| PS2 | secundary prompt |
| PWD | present working directory |
| SHELL | current shell |
| TERM | terminal type |
| UID | user ID |
| USER(NAME) | user name |
| VISUAL | your favorite full-screen editor |
| XENVIRONMENT | location of your personal settings for X behavior |
| XFILESEARCHPATH | paths to search for graphical libraries |
A lot of variables are not only predefined but also preset, using configuration files. We discuss these in the next section.
When entering the ls -al command to get a long listing of all files, including the ones starting with a dot, in your home directory, you will see one or more files starting with a . and ending in rc. For the case of bash, this is .bashrc. This is the counterpart of the system-wide configuration file /etc/bashrc.
When logging into an interactive login shell, login will do the authentication, set the environment and start your shell. In the case of bash, the next step is reading the general profile from /etc, if that file exists. bash then looks for ~/.bash_profile, ~/.bash_login and ~/.profile, in that order, and reads and executes commands from the first one that exists and is readable. If none exists, /etc/bashrc is applied.
When a login shell exits, bash reads and executes commands from the file ~/.bash_logout, if it exists.
This procedure is explained in detail in the login and bash man pages.
Let's look at some of these config files. First /etc/profile is read, in which important variables such as PATH, USER and HOSTNAME are set:
debby:~>cat /etc/profile
# /etc/profile
# System wide environment and startup programs, for login setup
# Functions and aliases go in /etc/bashrc
# Path manipulation
if [ `id -u` = 0 ] && ! echo $PATH | /bin/grep -q "/sbin" ; then
PATH=/sbin:$PATH
fi
if [ `id -u` = 0 ] && ! echo $PATH | /bin/grep -q "/usr/sbin" ; then
PATH=/usr/sbin:$PATH
fi
if [ `id -u` = 0 ] && ! echo $PATH | /bin/grep -q "/usr/local/sbin" ; then
PATH=/usr/local/sbin:$PATH
fi
if ! echo $PATH | /bin/grep -q "/usr/X11R6/bin" ; then
PATH="$PATH:/usr/X11R6/bin"
fi
|
These lines check the path to set: if root opens a shell (user ID 0), it is checked that /sbin, /usr/sbin and /usr/local/sbin are in the path. If not, they are added. It is checked for everyone that /usr/X11R6/bin is in the path.
# No core files by default ulimit -S -c 0 > /dev/null 2>&1 |
All trash goes to /dev/null if the user doesn't change this setting.
USER=`id -un` LOGNAME=$USER MAIL="/var/spool/mail/$USER" HOSTNAME=`/bin/hostname` HISTSIZE=1000 |
Here general variables are assigned their proper values.
if [ -z "$INPUTRC" -a ! -f "$HOME/.inputrc" ]; then
INPUTRC=/etc/inputrc
fi
|
If the variable INPUTRC is not set, and there is no .inputrc in the user's home directory, then the default input control file is loaded.
export PATH USER LOGNAME MAIL HOSTNAME HISTSIZE INPUTRC |
All variables are exported together.
for i in /etc/profile.d/*.sh ; do
if [ -r $i ]; then
. $i
fi
done
unset i
|
All readable shell scripts from the /etc/profile.d directory are read and executed. These do things like enabling color-ls, aliasing vi to vim, setting locales etc. The temporary variable i is unset to prevent it from disturbing shell behavior lateron.
Then bash looks for a .bash_profile in the user's home directory:
debby:~>cat .bash_profile
#################################################################
# #
# .bash_profile file #
# #
# Executed from the bash shell when you log in. #
# #
#################################################################
DO_FORTUNE=1
source ~/.bashrc
source ~/.bash_login
case "$OS" in
IRIX)
stty sane dec
stty erase
;;
# SunOS)
# stty erase
# ;;
*)
stty sane
;;
esac
if test "$DO_FORTUNE" -a ! -f "$HOME/.hushlogin"; then
"$DO_FORTUNE"
fi
|
First, a variable is set that is used in the end to determine whether or not to display a fortune cookie. If the user has a ~/.hushlogin file, which silences the login process (see man login), nothing is done. Then (this is a general config file for use on multiple architectures) terminal line settings are defined for operating systems that might make a fuss about them.
The ~/.bash_login file defines default file protection by setting the umask value. The ~/.bashrc is used to define a bunch of user-specific aliases and functions and personal environment variables. It first reads /etc/bashrc, which describes the default prompt (PS1) and the default umask value. After that, you can add your own settings. If no ~/.bashrc exists, /etc/bashrc is read by default:
debby:~>cat /etc/bashrc
# /etc/bashrc
# System wide functions and aliases
# Environment stuff goes in /etc/profile
# by default, we want this to get set.
# Even for non-interactive, non-login shells.
if [ `id -gn` = `id -un` -a `id -u` -gt 99 ]; then
umask 002
else
umask 022
fi
# are we an interactive shell?
if [ "$PS1" ]; then
if [ -x /usr/bin/tput ]; then
if [ "x`tput kbs`" != "x" ]; then # We can't do this with "dumb" terminal
stty erase `tput kbs`
elif [ -x /usr/bin/wc ]; then
if [ "`tput kbs|wc -c `" -gt 0 ]; then # We can't do this with "dumb"\
terminal
stty erase `tput kbs`
fi
fi
fi
case $TERM in
xterm*)
if [ -e /etc/sysconfig/bash-prompt-xterm ]; then
PROMPT_COMMAND=/etc/sysconfig/bash-prompt-xterm
else
PROMPT_COMMAND='echo -ne "\033]0;${USER}@${HOSTNAME%%.*}:${PWD/$HOME/~}\007"'
fi
;;
*)
[ -e /etc/sysconfig/bash-prompt-default ] && PROMPT_COMMAND=/etc/sysconfig/\
bash-prompt-default
;;
esac
[ "$PS1" = "\\s-\\v\\\$ " ] && PS1="[\u@\h \W]\\$ "
if [ "x$SHLVL" != "x1" ]; then # We're not a login shell
for i in /etc/profile.d/*.sh; do
if [ -x $i ]; then
. $i
fi
done
fi
fi
|
Upon logout, the commands in ~/.bash_logout are executed, which can e.g. clear the terminal.
Let's take a closer look at how these scripts work in the next section. Keep man bash close at hand.
The Bash prompt can do much more than displaying such simple information as your user name, the name of your machine and some indication about the present working directory. We can add other information such as the current date and time, number of connected users etc.
Before we begin, however, we will save our current prompt in another environment variable:
[jerry@nowhere jerry]$ MYPROMPT=$PS1 [jerry@nowhere jerry]$ echo $MYPROMPT [\u@\h \W]\$ [jerry@nowhere jerry]$ |
When we change the prompt now, e.g. issuing the command PS1="->", we can always get our original prompt back with the command PS1=$MYPROMPT. You will, of course, also get it back when you reconnect, as long as you just fiddle with the prompt on the command line and avoid putting it in a shell configuration file.
In order to understand these prompts and the escape sequences used, we refer to the Bash Info or man pages.
export PS1="[\t \j] "
Displays time of day and number of running jobs
export PS1="[\d][\u@\h \w] : "
Displays date, user name, host name and current working directory. Note that \W displays only base names of the present working directory.
export PS1="{\!} "
Displays history number for each command.
export PS1="\[\033[1;35m\]\u@\h\[\033[0m\] "
Displays user@host in pink.
export PS1="\[\033[1;35m\]\u\[\033[0m\] \[\033[1;34m\]\w\[\033[0m\] "
Sets the user name in pink and the present working directory in blue.
export PS1="\[\033[1;44m\]$USER is in \w\[\033[0m\] "
export PS1=...
Variables are exported so the subsequently executed commands will also know about the environment. The prompt configuration line that you want is best put in your shell configuration file, ~/.bashrc.
If you want, prompts can execute shell scripts and behave different under different conditions. You can even have the prompt play a tune every time you issue a command, although this way it gets boring pretty soon. More information can be found in the Bash-Prompt HOWTO.
A shell script is, as we saw in the shell configuration examples, a text file containing shell commands. When such a file is used as the first non-option argument when invoking Bash, and neither the `-c' nor `-s' option is supplied, Bash reads and executes commands from the file, then exits. This mode of operation creates a non-interactive shell. When Bash runs a shell script, it sets the special parameter `0' to the name of the file, rather than the name of the shell, and the positional parameters are set to the remaining arguments, if any are given. If no additional arguments are supplied, the positional parameters are unset.
A shell script may be made executable by using the chmod command to turn on the execute bit. When Bash finds such a file while searching the PATH for a command, it spawns a sub-shell to execute it. In other words, executing
filename ARGUMENTS
is equivalent to executing
bash file_name ARGUMENTS
if `filename' is an executable shell script. This sub-shell reinitializes itself, so that the effect is as if a new shell had been invoked to interpret the script, with the exception that the locations of commands remembered by the parent (see hash in the Info pages) are retained by the child.
Most versions of UNIX make this a part of the operating system's command execution mechanism. If the first line of a script begins with the two characters `#!', the remainder of the line specifies an interpreter for the program. Thus, you can specify bash, awk, perl or some other interpreter or shell and write the rest of the script file in that language.
The arguments to the interpreter consist of a single optional argument following the interpreter name on the first line of the script file, followed by the name of the script file, followed by the rest of the arguments. Bash will perform this action on operating systems that do not handle it themselves.
Bash scripts often begin with `#! /bin/bash' (assuming that Bash has been installed in `/bin'), since this ensures that Bash will be used to interpret the script, even if it is executed under another shell.
A very simple script consisting of only one command, that says hello to the user executing it:
#!/bin/bash echo "Hello $USER" |
The script actually consists of only one command, echo, which uses the value of ($) the USER environment variable to print a string customized to the user issuing the command.
Another on-liner, used for displaying connected users:
#!/bin/bash who | cut -d " " -f 1 | sort -u |
Here is a script consisting of some more lines, that I use to convert wav-files to mp3-format using the lame command. The script first makes a list of all wav-files in the current directory and puts it in the variable LIST. Then it sets the new name for each file, and converts the file format. After that, it cleans up the wav-files which are replaced by their mp3 counterparts:
tille:~>cat /usr/local/bin/wav2mp3 #!/bin/bash # convert *.wav into *.mp3 LIST=$(ls *.wav) for i in $LIST; do ORIG=$i DEST=$(ls $i | cut -d "." -f 1).mp3 lame -h $ORIG $DEST echo "done converting $i, removing.." rm $i done |
The cut command is used in this example to separate the actual file name from the file name suffix. Just entering a line like mv *.wav *.mp3 won't work. An echo command was added in order to display some activity. echo's are generally useful when a script won't work: insert one after each doubted step and you will find the error in no time.
The /etc/rc.d/init.d directory contains loads of examples. Let's look at this script that controls the RedHat Interchange server (E-commerce server), which uses somewhat more advanced techniques:
#!/bin/sh
#
# Run control script for Interchange
# http://interchange.redhat.com/
#
# chkconfig: 345 96 4
# description: Interchange is a database access and HTML templating system focused on ecommerce
# processname: interchange
# pidfile: /var/run/interchange/interchange.pid
# config: /etc/interchange.cfg
# config: /var/lib/interchange/*/catalog.cfg
# Source function library.
. /etc/rc.d/init.d/functions
# See how we were called.
case "$1" in
start)
echo -n "Starting Interchange: "
daemon interchange -q
echo
touch /var/lock/subsys/interchange
;;
stop)
echo -n "Shutting down Interchange: "
killproc interchange
echo
rm -f /var/lock/subsys/interchange
rm -f /var/run/interchange/interchange.pid
;;
status)
status interchange
;;
restart)
$0 stop
$0 start
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
exit 1
esac
exit 0
|
First, with the . command a set of functions, used by almost all shell scripts in /etc/rc.d/init.d, is loaded. Then a case command is issued, which defines 4 different ways the script can execute. An example might be interchange start. The decision of which case to apply is made by reading the (first) argument to the script, with the expression $1.
When no compliant input is given, the default case, marked with *, is applied, upon which the script gives an error message. The case list is ended with the esac statement. In the start case the server program is started as a daemon, and a PID and lock are assigned. In the stop case, the server is traced down and stopped, the lock and the PID are removed, etc. Each case is defined in the functions file. Options, such as the daemon option, and functions like killproc, are also defined in this file. This setup is specific to the distribution used in this example. The initscripts on your system might use other functions, defined in other files, or functionality like start and stop might be included in the script directly
instead of using functions from other scripts to accomplish the same effect.Upon success, the script returns an exit code of zero to its parent.
This script is a fine example of using functions, which make the script easier to read and the work done faster. Note that they use sh instead of bash, to make them useful on a wider range of systems. On a Linux system, calling bash as sh results in the shell running in POSIX-compliant mode.
The bash man pages contain more information about combining commands, for- and while-loops and regular expressions, as well as examples.