System: Ubuntu 12.04 (precise)
Desktop Environment: Xfce 4.10, distributed by Xubuntu
As it often happens, an update or install messes up your system and in the process of fixing it you start researching and learning something new. Which is a good thing. Bad thing is that you sure have other plans when you are forced to work on the fix.
This time it was installation of Ruby enVironment Manager (rvm). Everything went smoothly at the time, but later I’ve noticed that my ~/.bashrc is no longer sourced. It turns out that rvm installed .bash_profile in my home dir (I did not have it before). Also the instructions suggested to run terminal in the login mode to make sure rvm is working. Now I know what happened and why, but it took me some time to figure it out. Below is what I learned and how I fixed and organized my shell environment in the process.
A bit of theory first. Your shell can be: login/non-login and interactive/non-interactive. These options make 4 possible combinations. Login shell is used when the user is required to login, e.g. via ssh – usually it is also an interactive shell. In desktop environment terminal is normally started as an interactive non-login shell, but it is possible to configure the terminal to start as a login shell (despite the name it does not ask for user/password when it starts). In Xfce: open a terminal and set the option using this path: Menu -> Edit -> Preferences -> General Tab -> “Run command as login shell”
Here is how bash deals with the 3 files which can be present in the home dir (derived from “man bash”). Interactive login shell searches files in this order (only the first found is executed):
~/.bash_profile
~/.bash_login
~/.profile
Interactive non-login shell uses only this one:
~/.bashrc
To avoid surprises I’ve put all custom settings into ~/.bashrc
and sourced it from ~/.bash_profile
Something like that:
# if running bash if [ -n "$BASH_VERSION" ]; then # include .bashrc if it exists if [ -f "$HOME/.bashrc" ]; then . "$HOME/.bashrc" fi fi
This way I get the same environment in both login and non-login shells.
After having done this I decided that I also want to have custom environment for the programs launched from the desktop environment (GUI). When Xfce session starts (right after you are authenticated in the initial login screen), Xfce tries to run the script from ~/.config/xfce4/xinitrc, if it is not found, it runs the script located in /etc/xdg/xfce4/xinitrc. Below is the sequence of calls when the user’s script is found:
root: /sbin/init
root: lightdm
root: lightdm –session-child 12 97
user: /bin/sh/ /home/user/.config/xfce4//xinitrc — /etc/X11/xinit/xserverrc
user: xfce4-session
The last process launches everything related to the desktop environment, and its children inherit the settings provided in xinitrc. To customize the GUI environment you are supposed to copy the system file to your home dir and modify it:
cp /etc/xdg/xfce4/xinitrc ~/.config/xfce4/xinitrc
Here is a part of the modified file:
# run xfce4-session if installed if which xfce4-session >/dev/null 2>&1; then # check if we start xfce4-session with ck-launch-session. this is only # required for starting from a console, not a login manager if test "x$XFCE4_SESSION_WITH_CK" = "x1"; then if which ck-launch-session >/dev/null 2>&1; then ck-launch-session xfce4-session else echo echo "You have tried to start Xfce with consolekit support, but" echo "ck-launch-session is not installed." echo "Aborted startup..." echo exit 1 fi else # set gui environment if [ -f "$HOME/.xfcebashrc" ]; then source "$HOME/.xfcebashrc" fi # start xfce4-session normally xfce4-session fi exit 0 fi
The snippet above is almost identical to the original system file, I’ve only inserted these lines:
# set gui environment if [ -f "$HOME/.xfcebashrc" ]; then . "$HOME/.xfcebashrc" fi
Note that the script is run under sh, not bash, so bash-specific commands like source would not work. In the code above ~/.xfcebashrc is sourced, and I can configure my GUI environment by making it look like this:
# Local Java installation path PATH=$HOME/bin/jdk1.6.0_31/bin:$PATH export JAVA_HOME=$HOME/bin/jdk1.6.0_31 # Android tools path PATH=$PATH:$HOME/bin/android-sdk-linux/tools:\ $HOME/bin/android-sdk-linux/platform-tools # Android NDK path PATH=$PATH:$HOME/bin/android-ndk # Add RVM to PATH for scripting PATH=$PATH:$HOME/.rvm/bin export PATH
Now I have to take care of two files when I want to change shell environment:
~/.xfcebashrc – for GUI – launched programs and
~/.bashrc – for the terminal
Having two files I can set these environments independently. Note that the GUI environment will change on the next GUI login.
And in the end some useful commands and resources found on the net.
To see which path (or other environment) is being used by your GUI programs, start “Run Program” from the System menu and run this (it will produce a message containing the GUI path):
xmessage $PATH
To check if the shell is login or non-login:
shopt login_shell
To check environment of a running process with a known PID (replace PID with a number):
xargs --null --max-args=1 echo < /proc/PID/environ
Useful resources:
http://wiki.debian.org/EnvironmentVariables
http://wiki.debian.org/DotFiles