Setting Environment Variables with Spaces in OS X 10.8 (Mountain Lion)…

For setting the PATH and other environment variables, see here, since it will explain the more appropriate place for them.

Unfortunately, the methods listed in the linked article above don’t work for environment variables with spaces, e.g. MAVEN_OPTS="-Djavax.net.ssl.trustStore=keystore -Djavax.net.ssl.trustStorePassword=password".

To set these, the variables have to be configured as launch daemon arguments.  Here is an example of a file I have:

$ cat /Library/LaunchDaemons/mavenopts.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
	"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Label</key>
	<string>MAVEN_OPTS</string>
	<key>ProgramArguments</key>
	<array>
		<string>launchctl</string>
		<string>setenv</string>
		<string>MAVEN_OPTS</string>
		<string>-Djavax.net.ssl.trustStore=keystore -Djavax.net.ssl.trustStorePassword=password</string>
	</array>
	<key>RunAtLoad</key>
	<true/>
	<key>KeepAlive</key>
	<true/>
	<key>LaunchOnlyOnce</key>
	<true/>
</dict>
</plist>

This file can also go in /System/Library/LaunchDaemons instead of /Library/LaunchDaemons, but this is more a matter of philosophy; either location will work.

For more details, here is the Apple documentation: http://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html#//apple_ref/doc/uid/10000172i-SW7-BCIEDDBJ

Setting Environment Variables in OS X 10.8 (Mountain Lion)

I was recently trying to install SQL Developer from Oracle and wanted the LDAP function to work, which would require the application to be aware of my ORACLE_HOME, but I could not get the application to become aware of the environment variable when I launched it from the Launcher or the Dock.

Shell Variables

For environment variables that are only going to be used in the terminal, setting them in the .bash_profile is sufficient.  I’m not totally sure how the OS X shell works, but it seems to only read the .bash_profile (doesn’t seem to read .bashrc etc.), and it reads it every time a new terminal is open.

Example

PATH=${PATH}:${HOME}/scripts; export PATH

The problem with using the .bash_profile is that applications in the GUI are not aware of the environment variables (e.g. in my case, launching SQL Developer from the command line activated the features I wanted, but clicking the icon from the Launcher did not activate the features).

Environment Variables

There used to be a mechanism for setting environment variables prior to Mountain Lion by setting them in the ${HOME}/.MacOSX/environment.plist file, but this doesn’t seem to work in Mountain Lion.  They have to be set via launchd in /etc/launchd.conf. Unfortunately, this can’t be done at a user level at the moment (${HOME}/.launchd.conf not currently supported), so it must be done at a global level (affects all users).

Example

setenv ORACLE_HOME /opt/oracle

Path Variables

There is one more way to set the PATH globally for all users. It can be done by adding directories that should be in the path into files under the /etc/paths.d directory.

Example

Here is an example of a file that might be called /etc/paths.d/oracle.

/opt/oracle/bin

Custom Bash Completion

Bash completion (pressing [TAB][TAB] after a certain command to get the possible options) is an awesome feature, as I’m sure many would agree.  Here’s how to set up a custom completion.

The Completion Function

Here is an example of a basic bash completion function.

# the bash completion function to execute
bash_completion_function() {
  # clear the COMPREPLY variable
  COMPREPLY=()

  # get the current argument on the command line
  CURRENT_ARG="${COMP_WORDS[COMP_CWORD]}"

  # the list of possible options
  OPTIONS="aaa a23 bbb b34 ccc c45 ddd d56 eee e67 fff f78"

  COMPREPLY=($(compgen -W "${OPTIONS}" -- "${CURRENT_ARG}"))
  return 0
}
  • COMPREPLY: this is the variable that will eventually hold the completion results (what is output after pressing [TAB][TAB])
  • COMP_WORDS: an array of words on the command line
  • COMP_CWORD: an index for the COMP_WORDS array; something like COMP_WORDS[COMP_CWORD - 1] can also be used to perform some logic based on words in different positions on the command line
  • compgen: this is where the magic happens; the argument to the -W flag holds the possible arguments, and the possibilities are chosen based on the CURRENT_ARG; to test the completion function, this can be run on the command line (e.g. compgen -W "aaa a32 bbb b34" a should output aaa a32)

Apply The Function To the Command

After the function is set up, it must be sourced. This can be done manually for testing or example purposes (e.g. . ./bash_completion_function) or can be put somewhere like ~/.bashrc so it is always available.

After the function is sourced, it must be linked to the command. This is done with the complete command. The command below is linking the bash_completion_function function to the test.sh script. Again, this can be done manually for testing or example purposes or can be put somewhere like ~/.bashrc so it is always available.

complete -F bash_completion_function test.sh

With this particular set up, executing ./test.sh a [TAB][TAB] should output aaa a32 as possible arguments.

bash_profile and bashrc

I can never remember whether to put my personal settings into the .bash_profile or .bashrc file. Below is what is provided in the bash man page regarding these two files.

~/.bash_profile
       The personal initialization file, executed for login shells
~/.bashrc
       The individual per-interactive-shell startup file

A login shell is a shell where an actual login is performed. This might be if an ssh connection made, or if the system is logged into via a desktop environment such as GNOME or KDE. I usually put environment variables such as PATH, LD_LIBRARY_PATH, etc. into the .bash_profile.

A non-login shell might be an instance of gnome-terminal or running bash while connected via ssh. I put terminal behaviours and other aliases such as set -o vi into .bashrc.

One might argue that everything could just be put into the .bash_profile since the entire environment would be set up at login, but here are two things to consider that might help distinguish the two files:

  1. While interacting with the desktop environment, will this setting be needed? e.g. If I am going only going to log in to my desktop and run a browser, I don’t care about settings like alias less="less -X".
  2. Would I want this setting applied every time I open a terminal? e.g. Think about a welcome message or the diagnostic information given when opening an ssh connection to an Ubuntu server. Would you want to see this only when you logged in to the server or every time you opened a terminal (example below)?
Welcome to Ubuntu 11.10 (GNU/Linux 3.0.0-16-server x86_64)

 * Documentation:  https://help.ubuntu.com/11.10/serverguide/C

  System information as of Fri Apr  6 23:30:59 CDT 2012

  System load:  0.7                Processes:           115
  Usage of /:   0.6% of 454.23GB   Users logged in:     0
  Memory usage: 32%                IP address for eth0: xxx.xxx.xxx.xxx
  Swap usage:   0%

  Graph this data and manage this system at https://landscape.canonical.com/
Last login: Mon Apr  2 23:11:18 2012 from xxx.xxx.xxx