Bash Startup Explained

Chances are that you’ve come to this page because just now you’ve been tearing your hair out trying to figure out some kind of problem with bash startup.

Maybe an environment variable is not being set in your bash environment and you don’t understand why. Maybe you shoved something into various bash startup files, or profiles, or files at random until it worked.

Either way, the point of this post is to lay out bash startup as simply as possible so you can get a handle on what’s going on.

Diagram

This flow chart summarises what happens when bash starts up.

Now let’s explain each part in more detail.

Login Shell?

The first choice is whether you are in a login shell or not.

A login shell is the first shell that you get when you log into a host for an interactive session. A login shell doesn’t require a username/password combination to be entered. You can force a login shell by adding a --login flag to a bash invocation, eg

bash --login

A login shell sets up your base environment when you first get a bash shell.

Interactive?

Next you determine whether the shell you have is interactive or not.

You can tell whether your shell is interactive by testing whether the PS1 variable exists (this variable sets up your prompt):

if [ "${PS1-}" ]; then
echo interactive
else
echo non-interactive
fi

or by seeing whether the -i option is set using the special hyphen bash variable -, eg:

$ echo $-

If the i character is in the output, then the shell is interactive.


Material here based on material from my book
Learn Bash the Hard Way.
Free preview available here.

hero

In a Login Shell?

If you’re in a login shell, then bash looks for the /etc/profile file and runs it if it exists.

Then, it goes looking any of these three files, in this order:

~/.bash_profile
~/.bash_login
~/.profile

When it finds one, it runs it, and skips the others.

In an Interactive Shell?

If you’re in an interactive non-login shell, then it’s assumed that you’ve already been in a login shell, and that your environment is set up and will be inherited.

In this case, the following two files are run, in order, and if they exist:

/etc/bash.bashrc
~/.bashrc

In Neither?

If you’re in neither a login nor an interactive shell, then your environment will be bare indeed. This causes a great deal of confusion (see below, cronjobs).

In this case, bash looks at your environment’s BASH_ENV variable, and sources the file that that’s set to.

Common Confusions and Rules of Thumb

Cronjobs

95% of the time I end up debugging bash startup because I’m wondering why a cronjob isn’t working as expected.

The damn thing runs fine when I run it on the command line but fails when run in a crontab.

The cause of this confusion is for two reasons:

  • Cronjobs are non-interactive
  • Unlike scripts run on the command line, cronjobs do not inherit your shell environment

Normally, you don’t notice or care that a shell script is non-interactive because the environment is inherited from your interactive shell. This means that your PATHs and aliases are all set up as you expect them to be.

This is why you have to set PATH so often on cronjobs like this:

* * * * * PATH=${PATH}:/path/to/my/program/folder myprogram

Scripts Calling Each Other

Another common confusion is caused when scripts that shouldn’t are set up to call each other. For example, /etc/profile might source ~/.bashrc.

Usually, this is because someone was trying to fix something and that seemed to do the job. Unfortunately when you need those different types of sessions to be separated you might have more problems to solve.

Sandbox Docker Image

To help experiment with shell startup I’ve created a Docker image that can be used to debug shell startup in a safe environment.

To run it:

$ docker run -n bs -d imiell/bash_startup
$ docker exec -ti bs bash

The Dockerfile is here.

To force a login to simulate a login shell, run:

$ bash --login

To determine whether you have the BASH_ENV variable set, run:

$ env | grep BASH_ENV

To help with debugging crontab behaviour, I’ve set the docker image up with a crontab running a simple script (in /root/ascript) every minute:

$ crontab -l
$ cat /var/log/script.log

Material here based on material from my book
Learn Bash the Hard Way.
Free preview available here.

hero

If you like this, you might like one of my books:
Learn Bash the Hard Way

Learn Git the Hard Way
Learn Terraform the Hard Way

LearnGitBashandTerraformtheHardWay

3 thoughts on “Bash Startup Explained

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.