The Lumber Room

"Consign them to dust and damp by way of preserving them"

Zsh/Bash startup files loading order (.bashrc, .zshrc etc.)

with 15 comments

If you have ever put something in a file like .bashrc and had it not work, or are confused by why there are so many different files — .bashrc, .bash_profile, .bash_login, .profile etc. — and what they do, this is for you.

The issue is that Bash sources from a different file based on what kind of shell it thinks it is in. For an “interactive non-login shell”, it reads .bashrc, but for an “interactive login shell” it reads from the first of .bash_profile, .bash_login and .profile (only). There is no sane reason why this should be so; it’s just historical. Follows in more detail.

For Bash, they work as follows. Read down the appropriate column. Executes A, then B, then C, etc. The B1, B2, B3 means it executes only the first of those files found.

+----------------+-----------+-----------+------+
|                |Interactive|Interactive|Script|
|                |login      |non-login  |      |
+----------------+-----------+-----------+------+
|/etc/profile    |   A       |           |      |
+----------------+-----------+-----------+------+
|/etc/bash.bashrc|           |    A      |      |
+----------------+-----------+-----------+------+
|~/.bashrc       |           |    B      |      |
+----------------+-----------+-----------+------+
|~/.bash_profile |   B1      |           |      |
+----------------+-----------+-----------+------+
|~/.bash_login   |   B2      |           |      |
+----------------+-----------+-----------+------+
|~/.profile      |   B3      |           |      |
+----------------+-----------+-----------+------+
|BASH_ENV        |           |           |  A   |
+----------------+-----------+-----------+------+
|                |           |           |      |
+----------------+-----------+-----------+------+
|                |           |           |      |
+----------------+-----------+-----------+------+
|~/.bash_logout  |    C      |           |      |
+----------------+-----------+-----------+------+

In more detail is this excellent flowchart from http://www.solipsys.co.uk/new/BashInitialisationFiles.html :

Typically, most users will encounter a login shell only if either:
* they logged in from a tty, not through a GUI
* they logged in remotely, such as through ssh.
If the shell was started any other way, such as through GNOME’s gnome-terminal or KDE’s konsole, then it is typically not a login shell — the login shell was what started GNOME or KDE behind your back when you logged in; things started anew are not login shells. New terminals or new screen windows you open are not login shells either. (Starting a new window in OS X’s Terminal.app seems to count as a login shell, though.)

So typically (or sooner or later), what you will encounter are non-login shells. So this case is what you should write your config files for. This means putting most of your stuff in ~/.bashrc, having exactly one of ~/.bash_profile, ~/.bash_login, and ~/.profile, and sourcing ~/.bashrc from it. If you have nothing that you specifically want to happen only for login shells, you can even symlink one of the three to ~/.bashrc. In fact, even if you do, it is probably a good idea to have only file, as follows:

# Bash customisation file

#General configuration starts: stuff that you always want executed

#General configuration ends

if [[ -n $PS1 ]]; then
    : # These are executed only for interactive shells
    echo "interactive"
else
    : # Only for NON-interactive shells
fi

if shopt -q login_shell ; then
    : # These are executed only when it is a login shell
    echo "login"
else
    : # Only when it is NOT a login shell
    echo "nonlogin"
fi

Almost everything should go in the “general configuration” section. There might be some commands (those which produce output, etc.) that you only want executed when the shell is interactive, and not in scripts, which you can put in the first “conditional section”. I don’t see any reason to use the rest. You can drop the “echo” lines, but keep the “:”s — they are commands which do nothing, and are needed if that section is empty.

You then need to have only file, and you can call this ~/.bashrc and do cd && ln -s .bashrc .bash_profile

For zsh: [Note that zsh seems to read ~/.profile as well, if ~/.zshrc is not present.]

+----------------+-----------+-----------+------+
|                |Interactive|Interactive|Script|
|                |login      |non-login  |      |
+----------------+-----------+-----------+------+
|/etc/zshenv     |    A      |    A      |  A   |
+----------------+-----------+-----------+------+
|~/.zshenv       |    B      |    B      |  B   |
+----------------+-----------+-----------+------+
|/etc/zprofile   |    C      |           |      |
+----------------+-----------+-----------+------+
|~/.zprofile     |    D      |           |      |
+----------------+-----------+-----------+------+
|/etc/zshrc      |    E      |    C      |      |
+----------------+-----------+-----------+------+
|~/.zshrc        |    F      |    D      |      |
+----------------+-----------+-----------+------+
|/etc/zlogin     |    G      |           |      |
+----------------+-----------+-----------+------+
|~/.zlogin       |    H      |           |      |
+----------------+-----------+-----------+------+
|                |           |           |      |
+----------------+-----------+-----------+------+
|                |           |           |      |
+----------------+-----------+-----------+------+
|~/.zlogout      |    I      |           |      |
+----------------+-----------+-----------+------+
|/etc/zlogout    |    J      |           |      |
+----------------+-----------+-----------+------+

Moral:
  For bash, put stuff in ~/.bashrc, and make ~/.bash_profile source it.
  For zsh, put stuff in ~/.zshrc, which is always executed.

[Note: This is assuming you care only about interactive shells (shells you can type at). If further you ever use non-interactive shells (like “ssh [host] [command]” which executes [command] on [host] and logs out immediately) and there is something you absolutely absolutely want executed first even for these cases (think carefully: they can interfere in strange ways and you may be able to do without them), then you can put such stuff in ~/.zshenv. For bash, put them in a file and set the value of BASH_ENV to the filename.]

Thanks to this blog post and the manpages for information and ideas.

About these ads

Written by S

Sun, 2008-03-30 at 19:43:56 +05:30

Posted in compknow

Tagged with , , ,

15 Responses

Subscribe to comments with RSS.

  1. […] files (~/.bashrc, ~/.bash_profile, ~/.bash_login), restart your terminal, and you are good to go. Here is a great article about how bash configs work. I put tgrep() in my .bashrc file, but also added […]

  2. […] Zsh/Bash startup files loading order (.bashrc, .zshrc etc.) (tags: sysadmin linux zsh) […]

  3. […] for example.) More generally, it’s happening because some command in your ~/.bashrc (or some other file sourced automatically) is expecting the shell to be interactive, when it’s not. The fix is to find out which […]

  4. […] Zsh/Bash startup files loading order (.bashrc, .zshrc etc.) « The Lumber Room […]

  5. Thanks for putting this all together in such a clear and informative way :)
    I’ve referred people to this blog post several times.

    Josh

    Wed, 2011-01-05 at 01:41:13 +05:30

    • You’re welcome, very glad it helped. That was exactly my purpose in putting this together; I have myself referred to it several times when I was at a new computer. :-)

      S

      Wed, 2011-01-05 at 07:07:07 +05:30

  6. This is an amazing reference.

    saikobee

    Mon, 2011-01-17 at 14:43:07 +05:30

  7. Great table; I found this very helpful!

    One minor quibble: you say “For zsh, put stuff in ~/.zshrc, which is always executed,” but .zshrc is not always executed. Do you mean .zshenv? Either way, I think there is a good reason to use both files: .zshenv should never write anything to stdout. If it does, then things like “ssh [myhost] [mycommand]” will return unexpected results.

    However, it may be necessary to set up some environment variables on [myhost] before running [mycommand] — this is what .zshenv is for! Anything else, particularly anything that writes to stdout, should go in .zshrc.

    Drew Frank

    Thu, 2011-06-16 at 18:51:00 +05:30

    • [Edit: I’ve updated it, thanks.]

      Thanks. I guess when I wrote that, I only cared about interactive shells, and only wanted not to have to bother about login versus non-login shells. If you notice, the instructions for Bash similarly only cover interactive shells. :-) Actually, Bash doesn’t even have the equivalent of ~/.zshenv which is executed for non-interactive shells, so IMO it’s not crucial.
      I’m not convinced there is really anything I absolutely want done on remote systems when running things like “ssh [myhost] [mycommand]”, but yes, if someone wants it then it’s a good idea to mention it in the post as well.

      S

      Fri, 2011-06-17 at 00:49:30 +05:30

  8. Awesome article, I’ve been looking for this. People are lazy to read manuals etc… It can’t be more simple. Thanks.

    Ahmet Yasin Uslu

    Fri, 2011-08-12 at 05:48:58 +05:30

  9. it helped me lot. thanks

    Anonymous

    Mon, 2011-11-07 at 16:37:12 +05:30

  10. thanks a lot. the post is informative.

  11. There is a wonderful diagram of the Bash initialization process here: http://www.solipsys.co.uk/new/BashInitialisationFiles.html?HN0811

    Jonas Gorauskas

    Mon, 2012-09-03 at 08:01:48 +05:30

    • Really cool. Thanks I’ve gone ahead and added it to the article.

      S

      Mon, 2012-09-03 at 10:44:19 +05:30

  12. Nice start, but you still need to account for /etc/bashrc, /etc/bashrc.local, /etc/bash.bashrc.local, /etc/rc.common, /etc/rc.conf, /etc/rc.local, and more, depending on system. :-) E.g., for bash on Mac OS X 10.8, /etc/rc.common loads first, then /etc/profile then /etc/bashrc (there are no bash.*), etc.

    Stanton McCandlish

    Tue, 2013-10-22 at 13:54:33 +05:30


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 78 other followers