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.
26 Answers
TL;DR:
~/.bash_profileshould be super-simple and just load.profileand.bashrc(in that order)~/.profilehas the stuff NOT specifically related to bash, such as environment variables (PATHand friends)~/.bashrchas anything you'd want at an interactive command line. Command prompt,EDITORvariable, 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~/.bashrcmust not output anythingAnything that should be available only to login shells should go in
~/.profileEnsure that
~/.bash_logindoes not exist.
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:
~/.profilemust 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
~/.profileand~/.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$BASHor$BASH_VERSION.The separation between
*profileand*rcis 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~/.profileneeds to do it manually.
The simplest configuration would be:
Have a
~/.profilethat sets all environment variables (except bash-specific ones), perhaps prints a line or two, then sources~/.bashrcif 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
~/.bashrcthat performs any shell-specific setup, guarded with a check for interactive mode to avoid breaking things likesftpon Debian (where bash is compiled with the option to load~/.bashrceven 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
~/.bashrcfor such non-interactive logins. In this case, I've found it useful to move all environment variables (theexport ...lines) to a separate file,~/.environ, and to source it from both.profileand.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 setfi
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
~/.environtounset BASH_ENV.
Conclusion? Shells are a pain. Environment variables are a pain. Distribution-specific compile-time options are an immense pain in the ass.
6Have 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 | | |
+----------------+-----------+-----------+------+ 6 I offer you my "comprehensive" guidelines:
- Make
.bash_profileand.profileload.bashrcif 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. ;)
2I 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:
- Only run once, even when sourced repeatedly (use Include guards)
- 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
fiUnfortunately I don't remember how I came up with that, or why detecting an interactive shell wasn't sufficient.
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.