Bash shell: how to reconcile portable configuration files and GUI terminal start-up types (login or interactive)


In an attempt to "properly" implement a standard configuration of my preferred shell, bash, for use across multiple platforms, I've come across some confusion when dealing with terminals that are inconsistent with their choice of shell start-up types (login or interactive).

Bash Start-Up Files

From the bash manual, I've determined the difference between the start-up files and order/cases in which they are sourced (/etc/profile, ~/.bash_profile, ~/.bash_login, and ~/.profile for login shells, and ~/.bashrc for interactive non-login shells). The documentation suggested that you store your bash-specfic login configuration in ~/.bash_profile and your bash-specific interactive ones in your ~/.bashrc. The manual itself even suggested adding if [ -f ~/.bashrc ]; then source ~/.bashrc; fi to the ~/.bash_profile file so login shells inherit your interactive shell settings.

From what I've read in the manual and in various online forums/docs, it also seemed prudent to abstract your non-bash specific login configuration to ~/.profile since other shells can source this file. This may be an edge-case for people who may use Bourne shell in addition to bash (which is not really likely in my case), but it seems like a good habit to form.

So far this all seems pretty straight forward and logical, especially in an environment where the distinction between the various shell start-up types are apparent: on systems or terminals where the command-line is the primary interface, login shells are your primary session shells, interactive shells are subsequent shells launched in that session, and your non-interactive shells are script-running shells.

The real confusion is what constitutes a login shell when your shell is started from a terminal within a GUI environment where you are already authenticated to the system? It seems to me that the default behavior in Mac OSX is for the shell running in each window to start a login shell, whereas in xterm, each new window is an interactive shell.

It is possible that I am seeing a problem where one does not exist, however consider the following: the terminal situation suggests that I put the vast majority of my configuration in ~/.bashrc to avoid losing functionality should I be working on a terminal that starts bash as an interactive shell. Being unfamiliar with the intricacies of when bash might be spawned as interactive shells, I have to rely on this statement that abusing ~/.bashrc like this will eventually cause problems where login shell configuration being misplaced is detrimental to normal shell functioning. It isn't hard to imagine that problems might arise when you ignore the manual's advice and use configuration files inappropriately.

Since you can't really rely on using ~/.bashrc as your catch-all, it seems like you might need to treat your ~/.bash_profile and ~/.bashrc files differently depending on the terminal you are using. This seems like it would result in a lot of headaches and is counter to the purpose of having these distinct start-up files in the first place!

So finally my questions are as follows:

  • How do current Bash shell users address the issue of GUI terminal inconsistencies between starting login and interactive shells?
  • How do you create a single-set of bash start-up files for cross-platform (and cross-terminal) use, without running into problems?

Thank you for your assistance and advice.

Best Answer

The most common problem caused by misuse of shell startup files is that if you define environment variables in .bashrc as opposed to .profile, they are not defined in applications that are not launched from a shell in a terminal but rather from a GUI menu.

Defining environment variables from .bashrc is generally not a good idea, but that's primarily because it's generally useless as the variables should already have been set at login time. It can cause a problem in practice if you (or an application) have deliberately changed the value of a variable (e.g. PATH) in a program and you launch a shell from that program and expect the setting to have remained the same.

You can avoid the problem of environment variables being reset by defining a “sentinel” value, and not defining anything if the sentinel value is set:

if [ -z "$JMLANE_PROFILE_READ" ]; then
  export ...

Another problem caused by terminals starting login shells is that there are things that should only be done once when you log in, for example starting a password agent (e.g. ssh-agent), or initiating a session (e.g. running startx in certain circumstances). The sentinel variable avoids these issues.

A shell inside a terminal is always interactive. As long as you source .bashrc from .bash_profile when the shell is interactive, you don't need to worry about the terminal starting a login shell.

An additional wart of bash's startup file handling is that .bashrc is read by non-interactive shells invoked by rshd or sshd. For example, when you do rsync somefile, if your login shell on is bash, you can use .bashrc there to set the path to include rsync. You can tell you're in this situation because .bashrc is read from a non-interactive shell.

## .bashrc
if [[ $- != *i* ]]; then
  # either .bashrc was executed explicitly or this is a noninteractive rsh/ssh session