Obsah
Adjusting $PATH on macOS Mojave (and linking it to Xcode Command Line binaries)
Trying to set PATH environment variable on macOS is hell.
This article presents a simple solution using launchctl
utility which meets the following criteria:
- the PATH change is permanent (that is, it survives system restart);
- the PATH change is seen and propagated to both GUI and shell applications – that is, both Terminal (bash/Zsh in macOS Catalina+) and applications launched by Spotlight, Dock and Finder see the adjusted PATH;
- it allows appending a path to existing PATH variable, not just rewriting it to custom value;
- it should preferably allow setting the PATH on a per‑user basis, not system‑wide (and thus for all users);
- lastly, it works (at least) on macOS 10.14 Mojave and onwards
The article will also show how to make Xcode Command Line binaries available to all applications – this will, for example, replace the need to separately install python3 package (which is sometimes a little difficult), since it is part of the Command Line utilities.
Solution with launchctl config subcommand
The only — single — possible — working — way to set PATH environment variable is this:
- Per‑user $PATH change:
sudo launchctl config user path <new_path>
- System‑wide $PATH change:
sudo launchctl config system path <new_path>
To actually append a custom path to the existing $PATH variable, the command would look like this:
sudo launchctl config user path $PATH:<new_path>
The manual page man launchctl
is actually helpful here. It tells you that this command specifically works only for setting PATH environment variable. Setting any other environment variable is possible with another launchctl
sub‑command: launchctl setenv <variable> <new_value>
.
What it does not mention is the crucial information that while it is meant to set environment variables in general, the setenv
sub‑command cannot be used to set PATH variable. This information is even more important given that at some point before macOS Mojave, this was the preferred and working solution. Well, it does not work at least from macOS Mojave (reports differ as to precise OS X version on which it stopped working – see 1. 2,
3).
Caveat: this does not propagate changed $PATH to Terminal
The above solution is the only single one able to expose adjusted $PATH to GUI applications launched by Dock, Spotlight or Finder. However, launchctl config
fails to propagate this $PATH to shell environment under Terminal. So, to have the $PATH adjusted also under this environment, you have to add the same changed path to one of these locations:
/etc/paths
/etc/paths.d
– this is actually a folder, where you create new file with your custom path(s)~/.bash_profile
(probably works only until macOS Catalina+, which switched to Zsh – I guess that~./zshrc
will then work instead)
A good resource for these is here. I suggest using /etc/paths.d
, since it allows adding or removing your custom path without interfering with other paths at all (as with /etc/paths
) and it does not break with the change of default shell (as with ~/.bash_profile
).
How to revert to default $PATH
As Hannes Schmidt, the author of EnvPane (a great app for setting environment values) fittingly noted:
Amusingly, there is no documented way to revert to the defaults.
The changes to $PATH made by launchctl config
are stored in two different *.plist
files under the /private/var/db/com.apple.xpc.launchd/config/
directory:
- Per‑user $PATH changes:
/private/var/db/com.apple.xpc.launchd/config/user.plist
- System‑wide $PATH changes:
/private/var/db/com.apple.xpc.launchd/config/system.plist
So reverting to the default is possible by simply deleting these files (which requires sudo
authentication) and rebooting.
What does NOT work
Since there are almost dozen different solutions to the problem, it is I guess worth to list of all the ways which I personally tried and can confirm that they do not work (at least on macOS 10.14 Mojave):
So, trying to set custom $PATH in any of these files does NOT work (neither for GUI apps nor for Terminal):
~/.xprofile
~/.profile
~/.zshrc
(but see note for~/.bash_profile
below)~/.bashrc
~/.bash_login
/etc/xprofile
/etc/environment
/etc/launchd.conf
(by setting environment variable withsetenv
) – not working since Mac OS X 10.10 Yosemite~/launchd.conf
– a twist to the previous answer, this does not work either, as explained here together with the right answer since 10.10 Yosemite)~/.MacOSX/environment.plist
– not working either since Mac OS X 10.8 (pity, a nice Preference panel exists for setting it)Info.plist
file of the application (by setting PATH environment variable withLSEnvironment
key, as tried here and as recommended by Apple)- using bash script called by launch agent created in
~/Library/LaunchAgents/
– nicely utilized here, but not working since Mac OS X 10.12 Sierra
These files do NOT work to set custom $PATH for GUI applications, but they do set $PATH for Terminal only (and apps launched from it):
/etc/paths
/etc/paths.d
~/.bash_profile
(probably works only until macOS Catalina+, which switched to Zsh – I guess that~./zshrc
will then work instead)
The last two ways (/etc/paths.d
and ~/.bash_profile
) are explained here.
Also, here is a splendid list of ways to set PATH variable on different Unix/Linux‑like systems. Neither of them works on macOS Mojave, but it is a great reference for other systems (duh!). And here is a good attempt to list possibilities for macOS, albeit outdated and no longer valid.
Linking binaries from Xcode Command Line Tools to PATH (and thus installing python3 by doing so)
This task is now easy:
sudo launchctl config user path "$PATH:/Library/Developer/CommandLineTools/usr/bin"
And because Xcode Command Line Tools contain python3
package (v3.7.3 ships with Xcode Command Line Tools 11.3.1, which is the latest version working on Mojave), this will also install python3 for all applications on your system, including shell scripts run in Terminal and also GUI apps (for example, qBittorrent 4.2.2+ requires python3).
As explained above, making this change visible in Terminal will require you to supply the Command Line Tools path also to /etc/paths.d
. For this, the following command is enough:
sudo tee /private/etc/paths.d/90-Xcode-command_line_tools <<< "/Library/Developer/CommandLineTools/usr/bin"
The leading 90‑
in the filename causes the path to Command Line binaries to be added near the end of the $PATH construction. This is because the /private/etc/paths.d
folder is traversed in alphabetical order, so that alphabetically “later” files get their paths added to $PATH later. Thus, by putting “90‑” at the beginning of the filename, we push the path to generic Command Line binaries down the road to allow more specific sources of binaries to take precedence over them.
Remark on setting DYLD_* variables (e.g. DYLD_LIBRARY_PATH or DYLD_FALLBACK_LIBRARY_PATH)
One last note about a remotely related topic. You might want to use the above procedures to set custom path to your dynamic libraries (*.dylib
). This would make sense even for Xcode Command Line Tools, since they have not only their own binaries, but also dynamic libraries supplied in /Library/Developer/CommandLineTools/usr/lib
.
You would normally do this by adding a custom path to either DYLD_LIBRARY_PATH
or DYLD_FALLBACK_LIBRARY_PATH
.
TL;DR: Do not even try. Since OS X 10.11 El Capitan and later this is now absolutely impossible, since all DYLD_*
environment variables are cleared upon the launch of any executable for security reasons as part of System Integrity Protection (SIP). This means that setting these values is pointless, since they will be cleared anyway (that is, unless you disable SIP).
The alternative to using DYLD_* variables is to change the paths where the executable itself is looking for these dynamic libraries by using install_name_tool
, as explained in Tweaking apps, executables and dynamic libraries with install_name_tool and otool.