2009-02-12
Recognizing non-interactive shells and 'shell levels'
The issue of when bash sources your .bashrc does raise
the obvious question of how you can tell in your .bashrc what case
you're dealing with, so that you can do different things in each of
them.
The traditional approach to do this is to set environment variables to
signal what state you're in and how many things have been initialized.
One traditional 'have I been initialized' variable is $PS1, but this
has the tiny weakness that su'ing to the account from another account
that already has $PS1 set will not trigger your initializations.
(To keep clutter down, your 'am I a login shell' environment variable doesn't need to be exported, and indeed can be unset immediately after you use it. Other flag variables generally need to persist into future sub-shells.)
If you need to know interactive versus non-interactive, there are two
approaches. First, you can check the shell's built in $- variable to
see if the 'i' flag is part of the shell's flags, and second, you can
use test to see if standard input is connected to a terminal. Which
test is better depends in part on what you're trying to do; for example,
if you want to run stty, you really care only about whether you're
hooked up to a terminal.
The good news about modern systems is that all of this is much less important than it used to be; these days the overhead of re-doing redundant initializations, even relatively complex ones, is usually pretty low.
(Netbooks and other small machines may yet change this back, of course.)
2009-02-08
When bash sources your .bashrc
Since I just looked up the rules on when bash sources your .bashrc
(and did some testing), here's the rules on what gets sourced when,
for reasonably current versions of bash. Making this more confusing
is that bash apparently uses heuristics for some of its decisions.
- login shells do not source your
.bashrc; they source only your profile (bash looks at various names, but I just use.profile). - non-login interactive shells source your
.bashrc. - shells started by ssh to run remote commands should source your
.bashrc, assuming that bash got its heuristics right to recognize that this was happening. - otherwise, non-interactive shells (such as those running scripts)
do not source
.bashrc.
(Non-login interactive shells are common in today's environments, because
there are now a lot of things that start new shell sessions without
bothering to make them login shells; examples include the various X
terminal emulators (xterm et al) and screen. In fact in some
environments all of your shells will be non-login interactive shells,
and you won't really have a login shell as such.)
Various command line arguments can influence bash's idea of what its
environment is, or explicitly suppress bits of this; see --login,
--noprofile, and --norc.
Since shell options only affect the current shell, they need to be
set in your .bashrc so that they affect non-login interactive
shells. Environment variables such as $PATH could theoretically go
in your profile and be inherited by subshells, but most people put
them in their .bashrc too and just have their profile source their
.bashrc. Plus, this is the easiest way to alter your $PATH to make
things like 'ssh host private-command' work.
(On some systems this is quite important, because rsync is not on the
default $PATH. Solaris 10 is one example of this.)
As far as I can see, bash provides no clear sign about what sort of environment the shell session is running in. If this matters, you are left to deduce things through various signs, none of which are necessarily entirely accurate.
(This issue came up here recently, as we were trying to work out
what we needed in .bashrc and why on our Solaris 10 machines.
No one could remember exactly what had to be where.)