Environment variables in bash_profile or bashrc?

36

31

I have found this question [blog]: Difference between .bashrc and .bash_profile very useful but after seeing the most voted answer (very good by the way) I have further questions. Towards the end of the most voted, correct answer I see the statement as follows :

Note that you may see here and there recommendations to either put environment variable definitions in ~/.bashrc or always launch login shells in terminals. Both are bad ideas.

  1. Why is it a bad idea (I am not trying to fight, I just want to understand)?

  2. If I want to set an environment variable and add it to the PATH (for example JAVA_HOME) where it would be the best place to put the export entry? in ~/.bash_profile or ~/.bashrc?

  3. If the answer to question number 2 is ~/.bash_profile, then I have two further questions:

    3.1. What would you put under ~/.bashrc? only aliases?

    3.2. In a non-login shell, I believe the ~/.bash_profile is not being "picked up". If the export of JAVA_HOME entry was in bash_profile would I be able to execute javac & java commands? Would it find them on the PATH? Is that the reason why some posts and forums suggest setting JAVA_HOME and alike to ~/.bashrc?

    Thanks in advance.

Viriato

Posted 2012-04-06T04:15:25.653

Reputation: 483

Answers

26

On a modern system it is not especially common to run into the cases where it matters, but it does happen. (In particular, if you use shell operations in vim such as :r !command or the in-line !<motion>command form.)

What would you put under ~/.bashrc? only aliases?

You put things in ~/.bashrc that would not be inherited by subshells automatically; this means aliases and functions, mostly, although sometimes you have variable settings that you don't want visible outside the shell (this is very rare). It could be argued that those should be exported somehow, but various experimental attempts have run into compatibility issues with trying to hide them within the environment and have mostly been abandoned.

If I want to set an environment variable and add it to the PATH (for example JAVA_HOME) where it would be the best place to put the export entry? in ~/.bash_profile or ~/.bashrc?

You put environment settings in ~/.bash_profile so that they are given sane initial settings. Sometimes you will want to override these (often this is done by complex environments such as Matlab or Cadence); if you put the environment settings in ~/.bashrc then shells run from within those environments will lose the environments' customizations, and things may not work properly as a result. This also applies if you use a package like modules, virtualenv, rvm, etc. to manage multiple development environments; putting your settings in ~/.bashrc means you can't run the environment you want from within your editor, but instead will be forced into the system default.

In a non-login shell, I believe the ~/.bash_profile is not being "picked up".

This is correct; you normally want the initial shell to be a login shell and any shells started under that one to not be login shells. If the initial shell is not a login shell, you won't have a default PATH or various other settings (including your JAVA_HOME example).

Most desktop environments launched from display managers (which is to say, the vast majority of graphical logins) do not set up a login environment for the entire desktop, so you are forced to run the initial shell in terminals as a login shell. This causes a number of problems (notably that the PATH and such available to programs run from e.g. panels is not set up properly, because the panel is not a terminal and has not run ~/.bash_profile), but is a reasonable compromise given that it is not always possible to sanely run ~/.bash_profile in the non-interactive environment at the beginning of a session started by a display manager, depending on its contents. It is sometimes suggested to place environment settings in ~/.bashrc instead of configuring a login shell instead; as discussed above, this works as long as you do not need to override that environment, and causes odd breakages once you do need to do so.

I recently helped diagnose an issue like this on OS X where a user who had placed settings in ~/.bashrc then later started using rvm and perlbrew saw odd behavior, because the environments set up by the two were "undone" by ~/.bashrc inside editors and sudo (which on OS X, unlike Linux, propagates the user's $HOME so that their ~/.bashrc was run by the root shell). Before trying to use those environments, there was no problem; on starting to use them, they were bewildered by the unexpected loss of their settings.

geekosaur

Posted 2012-04-06T04:15:25.653

Reputation: 10 195

1I think I understand, I might have to read it more times to internalize it more but I am concluding the following. In enterprise environments in order to have finer control of the customized shells without side effects of the global one it is best practice to put environment variables in ~/.bash_profile. In a personal environment like Ubuntu or Linux Mint in order to have the PATH properly set I should set it under ~/.bashrc (or even in /etc/profile). Am I correct? – Viriato – 2012-04-06T05:00:29.293

It has less to do with enterprise environments than with whether you are just a user or a developer; the systems like modules and rvm are developer tools, as are Matlab and Cadence for somewhat different definitions of "developer". Simple development also doesn't require them, but when you need to test against multiple versions of Ruby, Perl, or Python then you really want something like rvm, perlbrew, and virtualenv (respectively) around to help keep it all straight. – geekosaur – 2012-04-06T05:22:13.443

2

to be honest, there is little difference these days despite what the guru had to say.

the issue behind this is that nowadays we login graphically rather than through a login shell. in the past, we unix users like to see a short report of what's going on a server immediately after login - then we will start X by command line - these report often requires some time to generate (e.g. 10-20 seconds). and then we don't want to see the same when we start e.g. xterm. thus the difference.

nowadays i don't think the distinction is any important now. i think these days if you source bashrc in bash_profile nobody could blame you.

note that this does not apply to macos x (every terminal.app started is a login shell)

bubu

Posted 2012-04-06T04:15:25.653

Reputation: 9 283

Agree with @bubu answer here - any setup where ~/.bash_profile doesn't source ~/.bashrc is rather hard to work with and verging on broken. Graphical terminal apps mean it's simpler to just source ~/.bashrc and put all config in there. – RichVel – 2019-03-21T11:30:26.873

I am not exactly sure I fully understand, but at work when I log in through ssh that is a login shell, then bash_profile and bashrc get sourced so i guess in that instance it does not matter. But if i log in graphically (what does that mean)? like login into my personal ubuntu? – Viriato – 2012-04-06T04:44:30.857

1

Well, about "Graphical Logins", it depends on which *DM you use ...

With GDM (Gnome 3.18) I have this:

/etc/gdm/Xsession

#!/bin/sh   <= *important*

...

# First read /etc/profile and .profile
test -f /etc/profile && . /etc/profile
test -f "$HOME/.profile" && . "$HOME/.profile"
# Second read /etc/xprofile and .xprofile for X specific setup
test -f /etc/xprofile && . /etc/xprofile
test -f "$HOME/.xprofile" && . "$HOME/.xprofile"

So, ~/.profile gets sourced in login using /bin/sh and not /bin/bash

There are two cases

  1. /bin/sh is linked to /bin/bash but runs in "POSIX/Bourne" mode
  2. /bin/sh is /bin/dash (debian/ubuntu). Fastest but with less features (ShellShock support ;) )

So the /bin/sh profile is ~/.profile and not ~/.bash_profile, ~/.zprofile

This file should be used for "shell agnostic" settings, like path and environment variables.

NO executable program for login-only user interaction should be but here (mail check, fortune, etc ...)

the ~/.*rc are meant only for "interactive" sessions (aliases for instance ...)

There is a difference among bash and zsh for interactive login shells

bash sources only .bash_profile, while zsh sources in the order:

  1. ~/.zprofile
  2. ~/.zshrc
  3. ~/zlogin (here aliases defined in ~/.zshrc are available. in case of "interactive" + "login" shells

The right way to do ~/.bash_profile was answered here:

Difference between .bashrc and .bash_profile

if [ -r ~/.profile ]; then . ~/.profile; fi
case "$-" in *i*) if [ -r ~/.bashrc ]; then . ~/.bashrc; fi;; esac

To enable test (and profiling), you may use this

~/.bash_profile:

#!/bin/bash

# ------------------------------------------------
export _DOT_BASH_PROFILE_0=`date  --rfc-3339=ns`
# ------------------------------------------------

if [ -f ~/.profile ] ; then
    . ~/.profile
fi

case "$-" in *i*) if [ -r ~/.bashrc ]; then . ~/.bashrc; fi;; esac

# ------------------------------------------------
export _DOT_BASH_PROFILE_1=`date  --rfc-3339=ns`
# ------------------------------------------------

~/.zprofile:

#!/bin/zsh

# ------------------------------------------------
export _DOT_ZSH_PROFILE_0=`date  --rfc-3339=ns`
# ------------------------------------------------

if [ -f ~/.profile ] ; then
    . ~/.profile
fi

# no need to source, zsh already handle ~/.zshrc

###case "$-" in *i*) if [ -r ~/.zshrc ]; then . ~/.zshrc; fi;; esac

# ------------------------------------------------
export _DOT_ZSH_PROFILE_1=`date  --rfc-3339=ns`
# ------------------------------------------------

then, to test:

chsh -s /bin/bash

ssh localhost
env

exit

ssh localhost env

ssh -t localhost bash -i -c env


chsh -s /bin/zsh

ssh localhost
env

exit

ssh localhost env

ssh -t localhost bash -i -c env

So RVM/virtualenv should go in ~/.profile, IMHO

But this DOES NOT WORK, sometimes ...

For instance, virualenvwrapper works only if the shell running Xsession is an "original" bash (exporting BASH_VERSION)

If you are on a dash system, the environment variable and path setting work, but virualenvwrapper function definition do not work because the script is not POSIX compliant.

The script doesn't give any error but it ends without any "workon" definition.

So you can set the environment at hand in ~/.profile, just to enable the correct python execution from client started directly from X:

export VIRTUAL_ENV="/home/mike/var/virtualenvs/myvirtualenv"
export PATH="$VIRTUAL_ENV/bin:$PATH"
unset PYTHON_HOME

https://gist.github.com/datagrok/2199506

https://www.bountysource.com/issues/9061991-setting-up-your-computer-virtualenvwrapper-linux-all

But for virualenvwrapper you have two alternatives:

  1. source it in ~/.bash_profile or ~/.zprofile (or ~/.zlogin) when terminal acts as login shell
  2. include the script in ~/.bashrc or ~/zshrc

This means that X clients (emacs for instance) should be started from the terminal shell and not from the graphical one!

"I can't get no satisfaction ..."

hute37

Posted 2012-04-06T04:15:25.653

Reputation: 61

A complete different story is running services with systemd

Some possible alternatives are: writing a wrapper script, define environment in "service" definition file, dumping the environment in a "env" file to be sourced in a parent shell.

Things becomes trickier with RVM/virtualenv ... – hute37 – 2015-11-20T11:35:49.477