Where should I export an environment variable so that all combinations of bash/dash, interactive/non-interactive, login/non-login, will pick it up?

Here's the motivation for the question:

I'm using Ubuntu 12.04 LTS 2 with the Unity desktop. In my .bashrc file, I append several directories to my PATH variable and define a few environment variables, such as JAVA_HOME. When I launch applications from a terminal (running bash, my default shell), this works great, but for several of the shortcuts that use the Unity launcher, they run apps that seem to be defined to use #!/bin/sh, which is aliased to /bin/dash, and they don't pick up the contents of either ~/.bashrc or ~/.profile.

I suppose I could change all of these shortcuts to use /bin/bash instead of /bin/sh to force it to pick up the .bashrc changes, but that seems really hacky.

Given that Ubuntu 12.04 (by default) aliases /bin/sh to /bin/dash and that my default shell is /bin/bash, is there a single place where I can choose to modify the PATH and define environment variables if I want them to be present under all of these circumstances:

  1. Whenever I create a non-login bash shell (using the terminal in unity)
  2. Whenever I create a login bash shell (for example, logging in remotely over ssh)
  3. Whenever I use a Unity application launcher (given that the launcher uses /bin/sh).
  4. Whenever a cron job executes (given that SHELL=/bin/sh in /etc/crontab).

If I understand correctly, I'm guessing that:

  • (1)/(2) and (3)/(4) are different because (1)/(2) are bash and (3)/(4) are dash.
  • (1) and (2) are different because the files that bash chooses to load differs depending on whether or not it is a login shell.
  • (3) and (4) are different because (3) will come at some point after I've logged in (and hence ~/.profile will have been sourced by one of its parent processes, while (4) will come at some point when I'm not logged in, and hence ~/.profile will not have been read.

(I wouldn't be surprised if other factors matter, too, such as whether or not the shell is interactive, so there are probably more combinations that I haven't even anticipated...I'm happy to have my question "improved" in that case.)

I would expect that at some point, someone must have made some sort of guide that tells you how/where to modify environment variables in a shell-independent way (or at least a dash/bash compatible way)...I just can't seem to find the right search terms to locate such a guide.

Solutions or pointers to solutions greatly appreciated!

Updated:

  • Clarification: This is the default Ubuntu user created by the 12.04 installation process, so nothing fancy. It does have a ~/.profile (that explicitly sources ~/.bashrc), and the only ~/.bash* files present are .bashrc, .bash_history, and .bash_logout...so no there's no .bash_profile.
  • Emphasis on scope: I don't really care about any shells other than the default interactive shell (bash) and any script that happens to use /bin/sh (aliased to dash), so there's no need to complicate this with anything extra for tcsh/ksh/zsh/etc. support.

Shell invocation is a bit of a complicated thing. The bash and dash man pages has INVOCATION sections about this.

In summary they says (there is more detail in the man page, you should read it):

When bash is                   | it reads
-------------------------------|----------
login shell                    | /etc/profile and then the first of ~/.bash_profile, ~/.bash_login or ~/.profile that exists.
                               |
interactive non-login shell    | /etc/bash.bashrc then ~/.bashrc
                               |
non-interactive shell          | The contents of $BASH_ENV (if it exists)
                               |
interactive (as "sh")          | The contents of $ENV (if it exists)

-

When dash is                   | it reads
-------------------------------|---------
login shell                    | /etc/profile then .profile
                               |
interactive shell              | The contents of ENV (it it exists, can be set in .profile as well as in initial environment)

I don't know about other shells offhand as I never use any of them. Your best bet might be to set a couple environment variables to point at the common location script and manually source that (when appropriate) in the couple of cases that doesn't cover.


So, there are several ways to approach this. Many people will either:

a. Have one file that has things common to all of your sh-style shells, say .shcommon and in each of the .profile .bashrc .kshrc et cetra, just source this with . .shcommon

b. Put everything in .profile and source this from the other files.

Things that are needed for specific shells or for interactive vs non-interactive shells can then go in the appropriate file before sourcing .shcommon

Personally, I dislike managing multiple files. So, I use the following approach:

First, everything I need goes in .profile Since I do have some bash and ksh specific things, I determine the current shell name using the following:

# get name of current shell
# strip leading - from login shell
export SHELLNAME="${0#-}"

and then have commands for specific shells in something like the following (some would prefer a case statement).

if [ "$SHELLNAME" = 'bash' ]
then
    shopt -s checkwinsize

elif [ "$SHELLNAME" = 'ksh' ]
then
    stty erase ^?
fi

If I have commands that should only run in interactive shells, I use the following:

# check for interactive flag i in shell options $-
# in bash and ksh you could use the following, but breaks in dash
# if [[ $- == *i* ]]
if [ "$(echo $- | grep i)" != "" ]
then
  fortune
fi

Things that are common in all sh-style shells, e.g., PATH can just go at the top.

Then, I use symlinks to load this same file in all the sh-style shells:

ln -s .profile .bashrc
ln -s .profile .kshrc

A couple of side-notes, if you have a .bash_profile, then bash will load this instead of .profile but dash and ksh will still load .profile This may be part of your problem.

Also, you might want to consider using #!/bin/bash in your scripts instead of #!/bin/dash unless you really want POSIX compatible scripts. bash has a lot of extra features that are very nice and dash or bash invoked as sh will disable many of these features.

Also, the bash man page does a good job of explaining when .profile versus .bashrc gets loaded. Similar rules apply to ksh. dash loads .profile on login and allows you to load a file at the startup of interactive shells that is specified using the ENV environment variable in .profile (check the dash man page too and search for .profile).