os x system path

PUBLISHED ON JUN 19, 2017 — OSX, PATH

Setting $PATH on OS X is not as simple as it should be. There are two main environments where it needs to be set - shell sessions in a terminal and GUI applications.

Terminal Sessions

For terminal sessions it’s as easy at it is on any other unix. Mostly.

When you open a new terminal window, or ssh in, then depending on what sort of terminal you use either ~/.bashrc or ~/.bash_profile will be executed.

Setting the PATH in ~/.bashrc, with the normal idiom …

PATH=/usr/whatever/bin:$PATH
export PATH

… and importing that from ~/.bash_profile with this snippet …

if [ -f ~/.bashrc ]; then
   source ~/.bashrc
fi

… will give you a consistent PATH in all sorts of terminal session.

There’s a useful helper app, called path_helper, that will look in /etc/paths and at all the files in /etc/paths.d and append them to your PATH. I think it’s called by default from /etc/profile, so every user has those added to their PATH by default.

GUI Applications

GUI apps are a whole other thing. If you launch them manually from a shell session, e.g. with open '/Applications/Qt Creator.app', then they’ll inherit the environment, including the PATH, from that shell and everything will work.

But if they’re launched from the finder, from the dock or automatically after a reboot, they’ll only see the “system” environment. The “system” environment is pretty minimal, and really hard to edit.

With each release of OS X Apple have locked down access to the PATH of GUI apps more and more. For a while you could put what you wanted into ~/.MacOSX/environment.plist - that doesn’t work now. You could add a launch agent to ~/Library/LaunchAgents or /Library/LaunchAgents - that doesn’t work either. Or you could add a setenv command in /etc/launchd.conf - that doesn’t work now either.

Apple’s argument is that this is for “security” and that any GUI app that needs a PATH set should set it. Which isn’t entirely unreasonable. Except that it makes developer tools such as editors and IDEs unusable, as any build process launched from them won’t be able to find any commandline tools that are installed anywhere non-standard, not even those installed by brew in /usr/local/bin. Even if I tell the IDE to launch cmake using a full path, any commands it runs will then fail. And, worse, it’ll behave differently depending on how the IDE was launched.

The only blessed way to set $PATH, as of OS X Sierra / 10.12 is using sudo launchctl config system path /your/path:/goes/here.

That’s annoying, because you have to set the PATH all at once, so you can’t easily add or remove parts of it by adding or removing lines in ~/.bashrc. And you can’t take advantage of path_helper. And any PATH you set this way will also be set in shell sessions, so if you also set it in ~/.bashrc you’ll end up with duplicate entries.

And you need to run it with sudo, so it’s not something you can script around terribly easily.

So, I’ve hacked together an approach that keeps the convenience of setting PATH in ~/.bashrc but keeps the system wide path up to date too.

At the top of my ~/.bashrc I have this:

# Save a copy of the system wide path
ORIGPATH=$PATH
# Wipe the path and set it to a reasonable default
PATH=
eval `/usr/libexec/path_helper -s`

And at the bottom I have this:

# Remove duplicate entries from PATH, retaining order
PATH=$(echo $PATH|perl -F: -lape'$_=join":",grep!$s{$_}++,@F')

export PATH
if [ "$PATH" != "$ORIGPATH" ]; then
    echo PATH has changed, update with
    echo 'sudo launchctl config system path $PATH'
fi

That’ll check the system-wide PATH against the PATH set in your .bashrc and remind you to update the system-wide one if they don’t match.

And that seems to fix everything. At least until Apple breaks it again in High Sierra / 10.13.

Other environment variables

Need to set other environment variables for GUI apps too? Take a look at osx-env-sync.