In Leopard, Apple has introduced a new mechanism for managing and maintaining your system path ($PATH).
Previously (and in most current Linux environments) paths were managed by updating the PATH environment variable directly in either the system profile (/etc/profile) or your local profile (~/.bash_profile).
Commonly you had entries like:
export JAVA_HOME = /usr/lib/j2se/jdk1.5.0_13/ export PATH=$PATH:$JAVA_HOME/bin ...
In Leopard, you no longer have to modify the profile to make adjustments to system paths. Instead, you can put a simple text file containing a path entry (or entries) into /etc/paths.d/.
Each line in this file will be interpreted as a path and added automatically to the system path.
macos-gls000120:~ ajordens$ ls -al /etc/paths.d/ total 40 drwxr-xr-x 7 root wheel 238 Jan 22 18:51 . drwxr-xr-x 105 root wheel 3570 Jan 23 11:59 .. -rw-r--r-- 1 root wheel 13 Sep 23 20:53 X11 -rw-r--r-- 1 root wheel 16 Jan 22 18:58 groovy -rw-r--r-- 1 root wheel 15 Jan 22 18:58 maven -rw-r--r-- 1 root wheel 20 Jan 22 18:58 postgresql -rw-r--r-- 1 root wheel 15 Jan 22 18:58 scala macos-gls000120:~ ajordens$ cat /etc/paths.d/scala $SCALA_HOME/bin
It’s up to you whether you include an environment variable or specify the full path.
Enter Leopard
In order to take advantage of this new feature, your /etc/profile needs to invoke /usr/libexec/path_helper. Depending on your upgrade path to Leopard, your profile may or may not have been automatically updated.
Unfortunately mine wasn’t and I couldn’t find a definitive post telling what changes I had to make to the profile. I decided to write the procedure up so that anyone else wanting to take advantage of paths.d and path_helper could do so.
macos-gls000120:~ ajordens$ cat /etc/profile
# System-wide .profile for sh(1)
if [ "${BASH-no}" != "no" ]; then
[ -r /etc/bashrc ] && . /etc/bashrc
fi
export ANT_OPTS="-Xmx256M"
export POSTGRESQL_HOME="/opt/local/lib/postgresql82"
export CONFBASE=/usr/local/src/confluence-2.6.2
export HADOOP_HOME=/usr/local/src/hadoop-0.15.2
export GROOVY_HOME=/usr/local/src/groovy-1.5.1
export SCALA_HOME=/usr/local/src/scala-2.6.0-final
export MAVEN_HOME=/usr/local/src/maven-2.0.7
export JAVA_HOME="/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home/"
if [ -x /usr/libexec/path_helper ]; then
eval `/usr/libexec/path_helper -s`
fi
That’s basically it. I put the path_helper invocation at the end of the profile because I wanted to be able to substitute environment variables.
Interesting tip.
I’d be curious to know if there’s a possibility to easily switch some environment variables. Let’s say today I want to use Java 1.4 and Groovy 1.0, and that later one, working on another project, I want to use Java 5 and Groovy 1.5.
How shall I do?
Adam Jordens: The only problem with your solution is that there is little improvement over hardcoded paths in /etc/profile if you hardcode the environment variables instead.
The clean Leopard profile looks like this:
# >>> START
# System-wide .profile for sh(1)
if [ -x /usr/libexec/path_helper ]; then
eval `/usr/libexec/path_helper -s`
fi
if [ "${BASH-no}" != "no" ]; then
[ -r /etc/bashrc ] && . /etc/bashrc
fi
# <<< END
Similar stuff can be found in csh.login and .zprofile (I can post them if anyone is interested.
Guillaume Laforge: I would make a Java symlink pointing to Java-1.5 somewhere and only add Java/bin to the PATH (so when you switch the symlink you will run new binaries).
I agree that it’s not that much of a difference but you will only be hard-coding the paths in one location.
Personally (and prior to using paths.d in Leopard) I’d just do my all my exports and PATH setup in the /etc/profile or .bash_profile directly.
For a lot of frameworks, you will need to define a FRAMEWORK_HOME environment variable in addition to simply including it on the path. In these situations, I’d argue there’s a slight benefit.
And I agree w/ your suggestion to Guillaume, that’s a pretty standard approach to separating your VMs and is something Apple and the various Linux distros having been doing for awhile (/etc/alternatives/java, etc.).
Thanks for your post! I helped me a lot.
I used a vanilla (unmodified) /etc/profile as it is read only for me, and just added a nice little test file called cdrtools to /etc/paths.d and it worked like a charm! All I had to do was restart my terminal app (opening a new tab would have done the trick as well I assume.
Thanks!
Nevermind that previous comment…like a previous poster mentioned path helper is automatically initialized in the /etc/profile
What’s wacky about this whole scheme is that you have no control over load order, so if you (for example) use MacPorts, how do make /opt/local/bin be the first part of your $PATH?
Lorin: You can add paths to /etc/paths instead, if you care about the order.
I had also the problem of wanting /usr/local/bin be first in the path before the defaults. My issue was that I wanted to override a default install of svn and use a newer installed in /usr/local/bin. But the problem is the same as for you.
I played around w/ “/etc/path.d” and edited “/etc/paths” itself adding always “/usr/local/bin/” on top but it would always get listed after the defaults.
I looked at “/etc/paths” and found the following entries:
/usr/bin
/bin
/usr/sbin
/sbin
/usr/local/bin
I had in “/etc/paths.d/” only the file “X11″ which contained one line: “/usr/X11/bin/”
doing a “set” or “echo $PATH” from the command line would return:
PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin
So it was clear (also explained in the man page for path_helper) that the content of of the paths.d directory would always be appended to what was found in “paths” itself.
I placed “/usr/local/bin” at top of paths but the order didn’t change. I logged out and back in and even rebooted to make sure there is nothing else causing this behavior but it always remained in the same order.
If I removed “/usr/local/bin” it was gone from the path but the rest stayed as is.
I had the suspicion that the content of “/etc/paths” didn’t really matter for the default entries of “/usr/bin:/bin:/usr/sbin:/sbin” and took them completely out of “/etc/paths” and logged into another shell to prove it. And sure enough, even w/o the entries the path variable was the same.
Therefore here is the order it apparently uses when it calls “path_helper” from /etc/profile at login:
1) System defaults
2) /etc/paths content aside system defaults
3) /etc/paths.d/ content
Apple wanted apparently to play save and not allow users to override system defaults and evtl. cripple the system.
I finally solved the problem by adding
export PATH=/usr/local/bin/:$PATH;
before the path_helper call in /etc/profile:
if [ -x /usr/libexec/path_helper ]; then
eval `/usr/libexec/path_helper -s`
fi
Now “/usr/local/bin” is searched first
Something to note is that the starting path is read from ~/.MacOSX/environment.plist which you can manipulate:
defaults read ~/.MacOSX/environment PATH
defaults write ~/.MacOSX/environment PATH “$PATH”
In Snow Leopard, moving “/usr/local/bin” to the top in “/etc/paths” now works without modifying “/etc/profile”.
You can just erase the default PATH entry before path_helper is called like this:
if [ -x /usr/libexec/path_helper ]; then
PATH=”"
eval `/usr/libexec/path_helper -s`
fi
Then you have full control in /etc/paths and /etc/paths.d. Do this in
/etc/profile
/etc/csh.login
/etc/zprofile