A post or two back, I looked at having BASH detect if I was on my “desktop” (for lack of a better word) or a server and decided the best approach was to hard-code that fact. I casually tossed the configuration in my .bashrc. Was that the right place?

BASH can potentially load any number of files at start up, and it’s not inherently obvious which are used when. There are three cases:

  • An interactive login shell
  • An interactive non-login shell
  • A non-interactive shell.

What on earth is the difference between a login and a non-login shell? This is an ancient UNIX concept. A login shell is one where the user is showed as logged in (added to utmp and wtmp). By default the user is also shown system information: their last login time, perhaps a Message of the Day, load averages, maybe notices of pending upgrade.

Non-login shells just start, no messages, and they do not get logged (though activity may be, if accounting is turned on), not appearing in the output of last or w.

The distinction was much more important when people used real terminals and things like terminal type and device options need to be set.

How a shell is invoked determines if it’s a login or non-login shell. Console and ssh logins are login shells. On the Mac new windows and tabs in both Terminal and iTerm default to login shells. tmux, screen, xterm, and sudo all create non-login shells by default. All of these can be configured to star either kind of shell.

An interactive BASH login shell looks for ~/.bash_profile, ~/.bash_login, then ~/.profile running the first one it finds. There’s no difference between ~/.bash_profile and ~/.bash_login, but ~/.profile is also used by the Bourne Shell. I find it’s best to avoid using ~/.profile as BASH is not backward compatible with it.

A non-login interactive shell runs only ~/.bashrc.

There’s almost no difference between login and non-login shells. I can’t think of anything in terms of functions, aliases, or environmental variable I wouldn’t want in both. If there’s any difference it would be that I might want to display something on login.

Given that, my setup is pretty simple. I put everything of importance in ~/.bashrc. I then create a ~/.bash_login that contains:

1
2
3
if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
fi

That way my login shells always load ~/.bashrc. If there are any things I want to run only at login, I can add them to the bash_login. I chose ~/.bash_login because that make it clear to me what it’s for.

However, this is not quite the Linux way. Most distros use the same general approach, but use ~/.profile as the login file. Because ~/.profile is also using by /bin/sh (typically dash), ~/.profile gets some logic to decide if it’s BASH or the vanilla Bourne shell.

1
2
3
4
5
6
7
# if running bash
if [ -n "$BASH_VERSION" ]; then
    # include .bashrc if it exists
    if [ -f "$HOME/.bashrc" ]; then
        . "$HOME/.bashrc"
    fi
fi

Things are also different on the Mac. Like LINUX, it’s default shell is BASH. However, when you create a new user on Mac OS, it does not get any BASH start up files. It’s a blank slate, which is always a nice place to start.

I prefer my approach to the Linux one for a couple of reasons, it doesn’t require special handling as ~/.bash_login is only run by BASH, if I really need Bourne shell configuration, I can keep it separate in my ~/.profile, and, as noted, I can tell by the name when ~/.bash_login is run. And when was the last time I used the Bourne shell anyway?

However, if the Linux approach appeals to you, use it, it’s functionally the same.

As a side note, I rarely use TCSH anymore, but it actually follows this pattern by default, login shells read ~/.login and all shells read ~/.tcshrc (or ~/.cshrc if it exists and ~/.tcshrc doesn’t).

And a final word of caution. Some programs will modify your dotfiles to add them selves to your path, or load functions (RVM for example) in to the shell. Sometimes they are smart at look at which files you are using. Other times, they just pick one, possibly creating something you didn’t have and breaking your set up. Something to keep a eye on.

And thus the question is answered, my local settings are in the right place!

Comments