Bash Startup Files
This is old hat but I can never remember it. Sometimes you just have to express things in your own words.
I tested this on Ubuntu 12.04 LTS but it should apply generally.
There are two axes: interactive / non-interactive, and login / non-login.
An interactive shell is one with which the user can interact, issuing commands etc. A shell running a script is not interactive. An interactive shell needs a prompt so it can prompt the user for input.
From the bash docs:
An interactive shell is one started without non-option arguments and without the -c
option whose standard input and error are both connected to terminals (as determined by
isatty(3)), or one started with the -i option. PS1 is set and $- includes i if bash is
interactive, allowing a shell script or a startup file to test this state.
How can you tell whether you’re in an interactive shell? There are a few ways.
The shell’s option flags (in
$-) will contain
i, which you can test like this:
if [[ $- == *i* ]]; then # interactive fi
help set for more information on the flags.
Alternatively you can look for the presence or absence of a prompt:
if [ -z "$PS1" ]; then # non-interactive fi
Finally you can test whether the shell is hooked up to a terminal (for user input), or to a socket (for ssh):
# 0 is the file descriptor for stdin if [[ -t 0 || -p /dev/stdin ]]; then # interactive fi
A login shell is started after you log in via the console, either sitting at the machine or remotely via ssh.
A shell launched from a login shell (e.g. with
$ /bin/bash), or with
su (but without the login option), is not a login shell.
If you open a new terminal window inside a GUI such as Gnome or KDE, you get a non-login shell. However OS X differs: it gives you a login shell for each new terminal window or tab.
A non-login shell generally inherits its parent shell’s environment.
From the bash docs:
A login shell is one whose first character of argument zero is a -, or one started with the
How can you tell whether you’re in a login shell? Check the shell options:
shopt | grep login_shell
login_shell on or
Your original shell in a terminal window is a login shell. By default a shell running a script is non-login, though you can change that with the
$ shopt | grep login_shell login_shell on $ cat foo.sh #!/bin/bash echo $(shopt | grep login_shell) $ ./foo.sh login_shell off $ bash -l foo.sh login_shell on
When do I get these shells?
After a successful login, you get an interactive login shell. This includes logging in via ssh.
If change user with the login option (
su - bob), you get an interactive login shell.
If you log in and then launch a new shell (
$ /bin/bash) or change user without the login option (
su bob), you get an interactive non-login shell.
If a shell script is running, it’s running in a non-interactive shell.
If you execute a command via ssh (
$ ssh email@example.com ls), it’ll execute in an interactive non-login shell.
|Login||After successful login (including via ssh).||When a shell script is running.|
|Non-login||After launching a child shell.
After changing user without login option.
Executing command over ssh.
|When a shell script is running.|
This is what happens by default.
For login shells (whether interactive or non-interactive), first
/etc/profile is sourced, then the first to be found of
For interactive non-login shells,
/etc/bash.bashrc is sourced and then
For non-interactive non-login shells, nothing is sourced.
A new user will have
~/.bashrc created for them.
~/.profile simply sources
~/.bashrc if it exists (regardless of whether or not the shell is interactive), and prepends
~/bin to the user’s PATH.
~/.bashrc sets up aliases, the prompt and so on.
The theory is that
~/.profile contains stuff you want to set up when you log in (once), and
~/.bashrc configures each (interactive) instance of the shell.
|Login||/etc/profile then [~/.bash_profile|~/.bash_login|~/.profile]||/etc/profile then [~/.bash_profile|~/.bash_login|~/.profile]|
|Non-login||/etc/bash.bashrc then ~/.bashrc||source $BASH_ENV if it exists|
Here’s a flowchart which includes the various flags you can use to modify the flow.
$ su bob # interactive non-login shell $ su - bob # interactive login shell $ exec su - bob # interactive login shell $ exec su - bob -c 'env' # non-interactive login shell $ ssh firstname.lastname@example.org # interactive login shell, `~/.profile` $ ssh email@example.com env # non-interactive non-login shell, `~/.bashrc`
Stick with the defaults. Put all your own configuration in
rbenv recommends addings its configuration to
~/.bash_profile. However that means it won’t be available to interactive non-login shells – such as those Capistrano executes.
So I put rbenv’s configuration in
~/.bashrc. This means that rbenv adds to the PATH every time a new shell is started but I haven’t found that to be a problem.