Upgraded to Leopard : Making use of /etc/paths.d and path_helper 12

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.

12 thoughts on “Upgraded to Leopard : Making use of /etc/paths.d and path_helper

  1. Reply Guillaume Laforge Jan 25, 2008 12:54 am

    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?

  2. Reply jpc Feb 28, 2008 3:24 pm

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

  3. Reply ajordens Mar 3, 2008 12:03 am

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

  4. Reply Jürgen Apr 7, 2008 11:16 am

    Thanks for your post! I helped me a lot.

  5. Reply Stephen Apr 22, 2008 4:29 pm

    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!

  6. Reply Stephen Apr 22, 2008 4:35 pm

    Nevermind that previous comment…like a previous poster mentioned path helper is automatically initialized in the /etc/profile

  7. Reply Lorin Rivers Jun 24, 2008 12:11 pm

    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?

  8. Reply Kamil Kisiel Jul 14, 2008 1:37 pm

    Lorin: You can add paths to /etc/paths instead, if you care about the order.

  9. Reply Renato Aug 2, 2008 12:35 pm

    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

  10. Reply Reid Ellis Mar 12, 2009 8:38 am

    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”

  11. Reply John Bledsoe Jan 6, 2010 8:52 pm

    In Snow Leopard, moving “/usr/local/bin” to the top in “/etc/paths” now works without modifying “/etc/profile”.

  12. Reply Timo Meinen Jan 19, 2010 12:14 am

    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

Leave a Reply