Choosing between .bashrc, .profile, .bash_profile, etc

206

178

This is embarrassing, but after many years of using POSIX systems full time, I still have a hard time figuring out if a shell customization should go in .bashrc, .profile, or somewhere else. Not to mention some of the OS-specific config files like .pam_environment.

Yes, I know how to puzzle through the documentation and learn when each file is or isn't loaded. What I'm wondering is if anyone has every put together comprehensive guidelines for how to decide which file to put a given type of customization in.

Avdi

Posted 2014-07-29T03:14:18.873

Reputation: 2 240

Question was closed 2014-07-31T04:59:54.903

6this question should not be marked as duplicate the reason is .profile is not available in the added question. – Premraj – 2015-12-20T02:25:00.330

Ans: http://serverfault.com/q/261802/270464

– Premraj – 2015-12-20T02:26:06.000

Answers

233

TL;DR:

  • ~/.bash_profile should be super-simple and just load .profile and .bashrc (in that order)

  • ~/.profile has the stuff NOT specifically related to bash, such as environment variables (PATH and friends)

  • ~/.bashrc has anything you'd want at an interactive command line. Command prompt, EDITOR variable, bash aliases for my use

A few other notes:

  • Anything that should be available to graphical applications OR to sh (or bash invoked as sh) MUST be in ~/.profile

  • ~/.bashrc must not output anything

  • Anything that should be available only to login shells should go in ~/.profile

  • Ensure that ~/.bash_login does not exist.

Dan Rabinowitz

Posted 2014-07-29T03:14:18.873

Reputation: 2 271

3+1, this allows ~/.profile to correctly set environment for services like GDM/LightDM/LXDM which explicitly run /bin/sh. – user1686 – 2014-07-29T06:15:32.663

13My .bashrc outputs quite a lot of stuff, can you comment on that? In particular, where should I put the greeting output? – Calimo – 2014-07-29T07:32:00.677

You deserve an upvote, this is how I do it, and it seems to be a sane way. – nyuszika7h – 2014-07-29T09:47:48.637

14@Calimo: Make it only output stuff in interactive mode. You can test for it using [[ $- == *i* ]], that is, looking for 'i' in the special $- variable. Of course, it only matters in the first place on systems where bash is compiled to read .bashrc in non-interactive mode. (That is, Debian but not Arch.) But it's a frequent cause of mysterious error messages when attempting to connect using sftp or scp or similar tools. – user1686 – 2014-07-29T11:20:08.760

4Now I've gotta know- why should .bash_login not exist? What does it do? – tedder42 – 2014-07-30T00:17:05.670

11@tedder42: It does the same as .bash_profile and .profile. But bash only reads the first one out of three. Meaning, if you have a .bash_login, then both .profile and .bash_profile will be mysteriously ignored. – user1686 – 2014-07-30T07:06:57.200

1@grawity bash will load .bash_profile and not .bash_login if they both exist, according to the man page & the linked duplicate question. So it's inaccurate to say to that .bash_login will cause .bash_profile to be ignored. – Kelvin – 2015-03-03T19:09:23.740

1In MacOS it seems like .profile will be ignored when starting a bash session if .bash_profile exist. – AkselA – 2017-07-13T19:09:12.443

1@grawity, Wtf? Who invented that sequence? – Pacerier – 2017-11-02T13:30:07.443

55

Over the last few years, I've had a lot of time to waste, so I have researched this for a bit more than just 10 minutes. I have no idea if this is the best layout, it's just one that happens to work correctly in pretty much all cases.

The requirements:

  • ~/.profile must be compatible with any /bin/sh – this includes bash, dash, ksh, whatever else a distro might choose to use.

  • Environment variables must be put in a file that is read by both console logins (i.e. a 'login' shell) and graphical logins (i.e. display managers like GDM, LightDM, or LXDM).

  • There is very little point in having both ~/.profile and ~/.bash_profile. If the latter is missing, bash will happily use the former, and any bash-specific lines can be guarded with a check for $BASH or $BASH_VERSION.

  • The separation between *profile and *rc is that the former is used for 'login' shells, and the latter every time you open a terminal window. However, bash in 'login' mode doesn't source ~/.bashrc, therefore ~/.profile needs to do it manually.

The simplest configuration would be:

  • Have a ~/.profile that sets all environment variables (except bash-specific ones), perhaps prints a line or two, then sources ~/.bashrc if being run by bash, sticking to sh-compatible syntax otherwise.

    export TZ="Europe/Paris"
    export EDITOR="vim"
    if [ "$BASH" ]; then
        . ~/.bashrc
    fi
    uptime
    
  • Have a ~/.bashrc that performs any shell-specific setup, guarded with a check for interactive mode to avoid breaking things like sftp on Debian (where bash is compiled with the option to load ~/.bashrc even for non-interactive shells):

    [[ $- == *i* ]] || return 0
    
    PS1='\h \w \$ '
    
    start() { sudo service "$1" start; }
    

However, there's also the problem that certain non-interactive commands (e.g. ssh <host> ls) skip ~/.profile, but environment variables would be very useful to them.

  • Certain distributions (e.g. Debian) compile their bash with the option to source ~/.bashrc for such non-interactive logins. In this case, I've found it useful to move all environment variables (the export ... lines) to a separate file, ~/.environ, and to source it from both .profile and .bashrc, with a guard to avoid doing it twice:

    if ! [ "$PREFIX" ]; then   # or $EDITOR, or $TZ, or ...
        . ~/.environ           # generally any variable that .environ itself would set
    fi
    
  • Unfortunately, for other distributions (e.g. Arch), I haven't found a very good solution. One possibility is to use the (enabled by default) pam_env PAM module, by putting the following in ~/.pam_environment:

    BASH_ENV=./.environ        # not a typo; it needs to be a path, but ~ won't work
    

    Then, of course, updating ~/.environ to unset BASH_ENV.


Conclusion? Shells are a pain. Environment variables are a pain. Distribution-specific compile-time options are an immense pain in the ass.

user1686

Posted 2014-07-29T03:14:18.873

Reputation: 283 655

2+1 for the last paragraph, but I prefer sourcing .profile and .bashrc from .bash_profile and keeping .profile clean. – nyuszika7h – 2014-07-29T09:52:31.927

@nyuszika7h: My .profile is clean, thanks.

– user1686 – 2014-07-29T11:18:48.770

1Note the comment re every time you open a window is the other way round for OSX – user151019 – 2014-07-29T12:20:46.693

1"There is very little point in having both ~/.profile and ~/.bash_profile": I disdagree. See Dan's answer for why. – rubenvb – 2014-07-30T07:25:18.853

@rubenvb Can you quote the relevant part? I think it's fine to have only a .profile and guard the bash-specific parts with conditionals. – Kelvin – 2015-03-03T19:28:03.887

36

Have a look at this excellent blog post by ShreevatsaR. Here's an extract, but go to the blog post, it includes an explanation for terms like "login shell", a flow chart, and a similar table for Zsh.

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      |           |      |
+----------------+-----------+-----------+------+

Flimm

Posted 2014-07-29T03:14:18.873

Reputation: 6 317

This is nice. It is important to note that usually /etc/profile calls /etc/bash.bashrc, and ~/.profile calls ~.bashrc. So effectively, /etc/bash.bashrc and ~/.bashrc are being executed for Interactive Logins as well. – wisbucky – 2017-09-22T18:30:48.773

Note that some distributions seem to override this scheme (with strange consequences) - see e.g. my bugreport to opensuse here: https://bugzilla.opensuse.org/show_bug.cgi?id=1078124

– Christian Herenz – 2018-01-30T14:45:17.703

Btw. at least with bash none of those files is being executed when the bash is being called via /bin/sh – JepZ – 2019-04-09T09:23:03.483

@JepZ You're right, that's what the third column "Script" explains. – Flimm – 2019-04-09T15:37:18.457

1@Flimm Well, the column 'Script' describes what happens when you start a non-interactive script via bash (e.g. /bin/bash). However, if you start a script via sh (and /bin/sh is a symlink to /bin/bash) none of the above is executed (not even BASH_ENV). The related paragraph of the bash man page can be found by searching for If bash is invoked with the name sh. – JepZ – 2019-04-09T17:02:15.253

21

I offer you my "comprehensive" guidelines:

  • Make .bash_profile and .profile load .bashrc if it exists, using e.g. [ -r $HOME/.bashrc ] && source $HOME/.bashrc
  • Put everything else in .bashrc.
  • Stop worrying.
  • Every four years or so, spend ten minutes researching this very question before giving up and going back to "not worrying".

EDIT: Added scare quotes to "comprehensive" just in case anyone is tempted to believe it. ;)

Mechanical Fish

Posted 2014-07-29T03:14:18.873

Reputation: 343

3Having both .bash_profile and .profile is a bit redundant; you only need the latter. You do need to make it /bin/sh-proof, though: if [ "$BASH" ] && [ -r ~/.bashrc ]; then . ~/.bashrc; fi, as there are programs (namely gdm/lightdm) that manually source the file from a /bin/sh script. This also means that environment kept in .bashrc would be ineffective. Had to -1, since your "comprehensive" guidelines will not work on many systems, as I had found out the hard way several times. – user1686 – 2014-07-29T06:13:47.143

No problem, I'd happily pay a -1 for an answer that is not merely tongue-in-cheek "comprehensive", and you've certainly earned that title. – Mechanical Fish – 2014-07-29T13:38:11.903

0

I gave up on trying to figure this one out and made one script (~/.shell-setup) which I source from all of the others.

This approach requires ~/.shell-setup to have two features:

  1. Only run once, even when sourced repeatedly (use Include guards)
  2. Don't generate any unwanted output (detect when output is ok)

#1 is pretty standard, although maybe not used much in shell scripts.

#2 is trickier. Here's what I use in bash:

if [ "" == "$BASH_EXECUTION_STRING" -a "" == "$DESKTOP_SESSION" ]; then
    echo "Hello user!" # ... etc
fi

Unfortunately I don't remember how I came up with that, or why detecting an interactive shell wasn't sufficient.

ShadSterling

Posted 2014-07-29T03:14:18.873

Reputation: 1 111

-2

Put everything in .bashrc and then source .bashrc from .profile

From the bash man page (on OS X 10.9):

When an interactive shell that is not a login shell is started, bash reads and executes commands from ~/.bashrc, if that file exists. This may be inhibited by using the --norc option. The --rcfile file option will force bash to read and execute commands from file instead of ~/.bashrc

The above text is why everything is put in .bashrc. However, there's a bit different behavior when you're dealing with a login shell. Again, quoting from the man page:

When bash is invoked as an interactive login shell, or as a non-interactive shell with the --login option, it first reads and executes commands from the file /etc/profile, if that file exists. After reading that file, it looks for ~/.bash_profile, ~/.bash_login, and ~/.profile, in that order, and reads and executes commands from the first one that exists and is readable. The --noprofile option may be used when the shell is started to inhibit this behavior.

.profile is read for login shells, but .bashrc is not. Duplicating all that stuff in .bashrc is bad™ so we need to source it in .profile in order for the behavior to remain consistent.

However, you don't want to source .bashrc from .profile unconditionally. Please see the comments and other answers for additional details.

mattr-

Posted 2014-07-29T03:14:18.873

Reputation: 181

4-1, DO NOT source .bashrc from .profile. See @DanRabinowitz's answer. – nyuszika7h – 2014-07-29T09:46:49.373

At least not unconditionally. – nyuszika7h – 2014-07-29T09:53:13.067

[ -n "$BASH" -a -f ~/.bashrc ] && . ~/.bashrc would be a sweet oneliner for .profile. – John WH Smith – 2014-07-29T10:55:22.417

@nyuszika7h, Why not? Everyone seems to suggest doing so.

– Pacerier – 2017-11-02T13:36:34.907