nl parser on large example; categories
|Deletions are marked like this.||Additions are marked like this.|
|Line 47:||Line 47:|
|Line 63:||Line 63:|
Dot Files used by bash in Debian
The default shell in Debian is bash, which is an extended clone of the Bourne shell. Unless otherwise specified, this documentation assumes you're using bash.
The way your Debian system behaves with respect to the reading of "dot files" at login time, setting up aliases, setting up environment variables, and so on is all highly dependent on how you actually log in.
Let's start with the simplest configuration: a local login on the Linux text console, without any graphical environment at all. In this case, when you boot the computer, you eventually see a "login:" prompt. This prompt is produced by a program called getty(8) which runs on the tty (terminal) device. When you type a username, getty reads it and passes it to the program called login(1). login reads the password database and decides whether it needs to ask you for a password. Once you've provided your password, login exec(2)s your login shell, bash.
Now, since bash is being invoked as a login shell (with name "-bash", a special ancient hack), it reads /etc/profile first. Then it looks in your home directory for .bash_profile, and if it finds it, it reads that. If it doesn't find .bash_profile, it looks for .bash_login, and if it doesn't find that, it looks for .profile (the standard Bourne/Korn shell configuration file). Otherwise, it stops looking for dot files, and gives you a prompt.
(Debian also has another layer called PAM which is relevant here. Before "execing" bash, login will read the /etc/pam.d/login file, which may tell it to read various other files such as /etc/environment. So you may have some extra enviroment variables, process limits, and so on, before your /etc/profile is read.)
You may have noted that .bashrc is not being read in this situation. You should therefore always have "source ~/.bashrc" at the end of your .bash_profile in order to force it to be read by a login shell.
Why is .bashrc a separate file from .bash_profile, then? This is done for mostly historical reasons, when machines were extremely slow compared to today's workstations. Processing the commands in .profile or .bash_profile could take quite a long time, especially on a machine where a lot of the work had to be done by external commands (pre-bash). So the difficult initial set-up commands, which create environment variables that can be passed down to child processes, are put in .bash_profile. The transient settings and aliases/functions which are not inherited are put in .bashrc so that they can be re-read by every subshell.
Let's take a moment to review. A system administrator has set up a Debian system and has the default locale setting "LANG=en_US" in /etc/environment. A local user named pierre prefers to use the fr_CA locale instead, so he puts "export LANG=fr_CA" in his .bash_profile. He also puts "source ~/.bashrc" in that same file, and then puts "set +o histexpand" in his .bashrc. Now he logs in to the Debian system by sitting at the console. login(1) (via PAM) reads /etc/environment and puts "LANG=en_US" in the environment. Then login "execs" bash, which reads /etc/profile and .bash_profile. The export command in .bash_profile causes the environment variable LANG to be changed from en_US to fr_CA. Finally, the source command causes bash to read .bashrc, so that the "set +o histexpand" command is executed for this shell. After all this, pierre gets his prompt and is ready to type commands interactively.
Now let's take the second-simplest example: an ssh login. This is extremely similar to the text console login, except that instead of using getty and login to handle the initial greeting and password authentication, sshd(8) handles it. sshd in Debian is also linked with PAM, so it will read the /etc/pam.d/ssh file (instead of /etc/pam.d/login). Otherwise, the handling is the same. Once sshd has run through the PAM steps, it "execs" bash as a login shell, which causes it to read /etc/profile and then either .bash_profile or .profile.
Let's suppose pierre (our console user) decides he wants to run X for a while. He types "startx", which invokes whichever set of X clients he prefers. startx is a wrapper around xinit, which runs the X server ("X"), and then runs through pierre's .xinitrc or .xsession file (if either one exists), or the system-wide default Xsession otherwise. Let's suppose pierre has the command "exec fluxbox" (and nothing else) in his .xsession file. When the smoke clears, he'll have an X server process running (as root), and a fluxbox process running (as pierre). fluxbox was created as a child of xinit, which was a child of startx, which was a child of bash, so it inherited pierre's LANG and other environment variables. When pierre launches something from the window manager's menu, that new command will be a child of fluxbox, so it inherits LANG, PATH, MAIL, etc. as well. In addition to all of that, fluxbox inherits the DISPLAY environment variable which tells it what X server to contact (in this case, probably ":0").
So what happens when pierre runs an xterm? fluxbox "forks" and "execs" an xterm process, which inherits DISPLAY, and so on. xterm contacts the X server, authenticates if necessary, and then draws itself on the display. In addition to DISPLAY, it inherited pierre's SHELL variable, which is probably /bin/bash, so it sets up a pseudo-terminal, then spawns a /bin/bash process to run in it. Since /bin/bash doesn't start with a "-", this one will not be a login shell. It will be a normal shell, which doesn't read /etc/profile or .bash_profile or .profile. Instead, it reads .bashrc which in our example contains the line "set +o histexpand". So his new xterm is running a bash shell, with all of his environment variables set (previously), and his shell option of choice has been enabled.
What we've seen so far is the normal Unix setup. Many people choose to run their systems this way, and it works well. It's also fairly simple to understand once you've been exposed to the basic concepts. If you want to change something, you know precisely what file to edit to make it happen -- aliases and transient shell options go in .bashrc, and environment variables, process limits, and so on go in .bash_profile. If you want to run various X client commands before your window manager or desktop environment is invoked (for example, "xterm &" or "xmodmap -e 'keysym Super_R = Multi_key'", you can put them in .xsession before the "exec" line.
However, some people like to have a graphical login (display manager), and that changes pretty much everything we've seen so far. Instead of getty and login, there's an xdm (or gdm or kdm or wdm or ...) process handling the authentication. And the biggest difference of all is that when our *dm process finishes authenticating the user, it doesn't "exec" a login shell. Instead, it "execs" an X session directly. Therefore, none of the "normal" user configuration files are read in at all -- no /etc/profile, no .bash_profile and no .profile. (But /etc/environment is still read in by PAM, assuming /etc/pam.d/*dm is configured to use pam_limits.)
Let's take xdm as an example. pierre comes back from vacation one day and discovers that his system administrator has installed xdm on the Debian system. He logs in just fine, and xdm reads his .xsession file and runs fluxbox. Everything seems to be OK until he gets an error message in the wrong locale! Since he overrides the LANG variable in his .bash_profile, and since xdm never reads .bash_profile, his LANG variable is now set to en_US instead of fr_CA.
Now, the naive solution to this problem is that instead of launching "xterm", he could configure his window manager to launch "xterm -ls". This flag tells xterm that instead of launching a normal shell, it should launch a login shell. Under this setup, xterm spawns /bin/bash but it puts "-/bin/bash" (or maybe "-bash") in the argument vector, so bash acts like a login shell. This means that every time he opens up a new xterm, it will read /etc/profile and .bash_profile (built-in bash behavior), and then .bashrc (because .bash_profile says to do that). This may seem to work fine at first -- his dot files aren't heavy, so he doesn't even notice the delay -- but there's a more subtle problem. He also launches a web browser directly from his fluxbox menu, and the web browser inherits the LANG variable from fluxbox, which is now set to the wrong locale. So while his xterms may be fine, and anything launched from his xterms may be fine, his web browser is still giving him pages in the wrong locale.
So, what's the best solution to this problem? There really isn't a universal one. A better approach is to modify the .xsession file to look something like this:
[ -r /etc/profile ] && source /etc/profile [ -r ~/.bash_profile ] && source ~/.bash_profile xmodmap -e 'keysym Super_R = Multi_key' xterm & exec fluxbox
This causes the shell that's interpreting the .xsession script to read in /etc/profile and .bash_profile if they exist and are readable, before running xmodmap or xterm or "execing" the window manager. However, there's one potential drawback to this approach: under xdm, the shell that reads .xsession runs without a controlling terminal. If either /etc/profile or .bash_profile uses any commands that assume the presence of a terminal (such as "fortune" or "stty"), those commands may fail. This is the primary reason why xdm doesn't read those files by default. If you're going to use this approach, you must make sure that all of the commands in your "dot files" are safe to run when there's no terminal.
One way to do that, but still retain those commands for use when you login with ssh, is to protect the relevant block of code with an "if" statement. For example:
## Sample .bash_profile export PATH=$HOME/bin:$PATH export MAIL=$HOME/Maildir/ export LESS=-X export EDITOR=vim VISUAL=vim export LANG=fr_CA # Begin protected block if [ -t 0 ]; then # check for a terminal [ x"$TERM" = x"wy30" ] && stty erase ^h # sample legacy environment echo "Welcome to Debian, $LOGNAME" /usr/games/fortune fi # End protected block [ -r ~/.bashrc ] && source ~/.bashrc