12

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.
7
  • 2
    Place it in one file then let other rc files source it.
    – konsolebox
    Commented Aug 23, 2013 at 18:58
  • According to the dash man page I have here it should be reading $HOME/.profile for login shells. Commented Aug 23, 2013 at 19:24
  • @konsolebox Which rc files? AFAICT, dash doesn't have an rc file. And as I stated, dash doesn't seem to be picking up the contents of ~/.profile.
    – Mickalot
    Commented Aug 23, 2013 at 21:36
  • @EtanReisner Yes, and this is the point of the question. When dash isn't running as a login shell (and hence ~/.profile isn't sourced), what then?
    – Mickalot
    Commented Aug 23, 2013 at 21:39
  • @Mickalot Are you running dash interactively? If not try to add the -l option to it.
    – konsolebox
    Commented Aug 23, 2013 at 21:49

3 Answers 3

12

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.

3
  • 1
    Sadly, the crux of this problem is the one that's missing from your dash table. When the Unity desktop invokes any script that begins with #!/bin/sh, it launches (what I assume is) a non-login, non-interactive dash shell. So the question is: how do I make the same environment variables available there that are present everywhere else in the matrix?
    – Mickalot
    Commented Aug 23, 2013 at 22:29
  • Right. I imagine that's why bash added BASH_ENV for that slot. Given that dash doesn't appear to have anything that works in that slot I'm not sure you can solve that problem without affecting the change in the environment of application that launches dash (such that dash inherits it). This would require the environment variables being set in your X environment which is where the .xsession(rc) comment from @Mickalot comes into play. This still leaves, as he indicated, item four left out (but I believe that's actually somewhat intentional). Commented Aug 23, 2013 at 23:25
  • Since I'm on Ubuntu 12.04 LTS, cron is actually pixie-cron, so I can define env variables in my crontab file...I'm guessing SHELL=/bin/bash and BASH_ENV=~/.bashrc should do it?
    – Mickalot
    Commented Aug 24, 2013 at 0:27
4

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).

9
  • Could you not just test $0 for the shell name? Are there shells/cases where that does not work? Commented Aug 23, 2013 at 19:43
  • Similarly, does checking for i in $- not suffice for the interactive shell test? (It will trigger on interactive shells without a tty I grant but that may or may not be an undesirable side-effect.) Commented Aug 23, 2013 at 19:50
  • @Etan: Hrm, I know that I first tried $0 and ran into problems... I can't seem to recall exactly what they were though. Logging directly into the console does show $0 as -bash instead of bash but that could be remedied.
    – idfah
    Commented Aug 23, 2013 at 20:05
  • @Etan: Yes, checking for i in $- would also work. I would have to think about when you would have an interactive shell without a tty? Your solution does have a better "feel" to it for some reason, I may change that.
    – idfah
    Commented Aug 23, 2013 at 20:08
  • The - in -bash means "login shell" but yes you would need to account for that in places where you test the value or want to display it to someone. Commented Aug 23, 2013 at 20:11
0

Since cases (1) and (2) are solved by sourcing my environment variables in .bashrc and .profile, the real question is "what's the name of the file where I source these same variables for (3) and (4).

It looks like there's an answer to part (3) of the question (how do I get the environment variable to be imported in the Unity desktop) on askubuntu. The suggestion there is to create an ~/.xsessionrc file that will be sourced by /etc/X11/Xsession. (I've tried this out, and it seems to work...yay!)

I'm still perplexed by what to do for (4). Surely, if I create a cron job (or daemon), I can replace '/bin/foo' with something like 'bash -i -c /bin/foo' to force it to use bash to load the right environment variables, but this also means that I'll have to tinker with any third-party tools that might install daemon tasks or cron job on my behalf. Yuk.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .