Environment variables in Mac OS X
Asked Answered
S

11

208

Update: The link below does not have a complete answer. Having to set the path or variable in two places (one for GUI and one for shell) is lame.

Not Duplicate of: Setting environment variables in OS X?


Coming from a Windows background where it's very easy to set and modify environment variables (just go to System Properties > Advanced > Environment Variables), it does not seem to be that straight forward on Mac OS 10.5. Most references say I should update /etc/profile or ~/.profile. Are those the equivalent of System Variables and User Variables? For example, where should I set my JAVA_HOME variable?


EDIT:

I want to be able to access the variable from the terminal as well as an app like Eclipse. Also, I hope I don't have to restart/logout to make this take effect.

Shabby answered 2/3, 2009 at 20:7 Comment(1)
And there is some advice in the answers to the link question not repeated here...Ague
C
160

There's no need for duplication. You can set environment variables used by launchd (and child processes, i.e. anything you start from Spotlight) using launchctl setenv.

For example, if you want to mirror your current path in launchd after setting it up in .bashrc or wherever:

PATH=whatever:you:want
launchctl setenv PATH $PATH

Environment variables are not automatically updated in running applications. You will need to relaunch applications to get the updated environment variables (although you can just set variables in your shell, e.g. PATH=whatever:you:want; there's no need to relaunch the terminal).

Cruck answered 21/9, 2010 at 1:20 Comment(12)
Looks like the best answer so far, no need for a 3rd party app!Shabby
This doesn't appear to be global: environment variables set this way are local to the user. We still don't have a global mechanism for setting an environment variable.Wyattwyche
@Wyattwyche What do you mean, local to the user? I would expect all processes subsequently started from launchd to be affected.Cruck
@MattCurtis: Environment variables set via launchctl setenv seem to only be visible to the user making the change. If I set a variable as an ordinary user, it's not visible to root (via sudo) and vice-versa.Wyattwyche
@Wyattwyche OK, root has its own launchd - ps aux | grep launchd will show this. Also check man sudo, which documents that sudo (by default) deliberately resets the environment - if you sudo -E it will preserve the environment (including variables you've set with launchctl setenv). Do you have an actual application for this, by the way? If so, does this method work for you?Cruck
@MattCurtis: The main application is whenever a GUI app needs an environment variable, and you want to set it for all users. Most Macs tend to be single-user, so admittedly in practice the non-global problem isn't a problem. (A concrete example is that IntelliJ IDEA (as of 10) needs M2_HOME to be set for maven integration to work properly. For the system maven this means /usr/share/maven. Given that this is system-wide I found it frustrating that I couldn't set it in a likewise manner.)Wyattwyche
@Wyattwyche In that case is /etc/profile not a reasonable solution for you? It will get sourced whenever a new user logs on. Looking after a multiuser system you're probably not wanting to monkey around with environment variables for existing sessions. Otherwise, you may need to identify all launchd processes and write a script - because you're looking at a programming website I'm guessing that's an acceptable solution :-)Cruck
@MattCurtis Sadly /etc/profile is only used for processes launched as children of a Bourne-shell-based process. GUI apps don't generally qualify. In summary we have ways to set environment variables for: a) All processes for a user (launchd or ~/.MacOSX/environment.plist); b) All Bourne-shell-based processes (/etc/profile); c) But not both.Wyattwyche
@Wyattwyche So do you have multiple GUI users logged on to the OSX desktop simultaneously? How does that work? In any case, why can't you use the mechanic from my answer (to reflect command line changes in launchd's PATH, which I believe is what the OP was asking for) to do (c) "both"? Or are you after a no-code solution? In that case maybe you will have more luck asking about this on superuser.Cruck
You need to also add 'launchctl export' at the end to actually export all launchctl setenv variables.Drake
@PeriodicMaintenance I don't think so. "launchctl export" is in the manpage but 10.10 launchctl says "Unrecognized subcommand: export". Besides, the manpage says it exports "for use in a shell eval statement" -- in the example I gave, PATH is already exported, "setenv" will set the variable in launchd's environment, so there's nothing further to export. But I guess -- if the subcommand existed -- you could use eval "$(launchctl export)" to export variables not already exported -- though you could just add "export" when setting the variable (e.g. export ANSWER=42.)Cruck
launchctl config system path $PATH will take care of root launchd. I believe launchctl config user path $PATH would be the preferred way for to set a persistent path for user launchd. (vs setenv).Kami
P
323

There are several places where you can set environment variables.

  • ~/.profile: use this for variables you want to set in all programs launched from the terminal (note that, unlike on Linux, all shells opened in Terminal.app are login shells).
  • ~/.bashrc: this is invoked for shells which are not login shells. Use this for aliases and other things which need to be redefined in subshells, not for environment variables that are inherited.
  • /etc/profile: this is loaded before ~/.profile, but is otherwise equivalent. Use it when you want the variable to apply to terminal programs launched by all users on the machine (assuming they use bash).
  • ~/.MacOSX/environment.plist: this is read by loginwindow on login. It applies to all applications, including GUI ones, except those launched by Spotlight in 10.5 (not 10.6). It requires you to logout and login again for changes to take effect. This file is no longer supported as of OS X 10.8.
  • your user's launchd instance: this applies to all programs launched by the user, GUI and CLI. You can apply changes at any time by using the setenv command in launchctl. In theory, you should be able to put setenv commands in ~/.launchd.conf, and launchd would read them automatically when the user logs in, but in practice support for this file was never implemented. Instead, you can use another mechanism to execute a script at login, and have that script call launchctl to set up the launchd environment.
  • /etc/launchd.conf: this is read by launchd when the system starts up and when a user logs in. They affect every single process on the system, because launchd is the root process. To apply changes to the running root launchd you can pipe the commands into sudo launchctl.

The fundamental things to understand are:

  • environment variables are inherited by a process's children at the time they are forked.
  • the root process is a launchd instance, and there is also a separate launchd instance per user session.
  • launchd allows you to change its current environment variables using launchctl; the updated variables are then inherited by all new processes it forks from then on.

Example of setting an environment variable with launchd:

echo setenv REPLACE_WITH_VAR REPLACE_WITH_VALUE | launchctl

Now, launch your GUI app that uses the variable, and voila!

To work around the fact that ~/.launchd.conf does not work, you can put the following script in ~/Library/LaunchAgents/local.launchd.conf.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>local.launchd.conf</string>
  <key>ProgramArguments</key>
  <array>
    <string>sh</string>
    <string>-c</string>
    <string>launchctl &lt; ~/.launchd.conf</string>    
  </array>
  <key>RunAtLoad</key>
  <true/>
</dict>
</plist>

Then you can put setenv REPLACE_WITH_VAR REPLACE_WITH_VALUE inside ~/.launchd.conf, and it will be executed at each login.

Note that, when piping a command list into launchctl in this fashion, you will not be able to set environment variables with values containing spaces. If you need to do so, you can call launchctl as follows: launchctl setenv MYVARIABLE "QUOTE THE STRING".

Also, note that other programs that run at login may execute before the launchagent, and thus may not see the environment variables it sets.

Premed answered 31/12, 2010 at 0:33 Comment(21)
Actually, regarding ~/.MacOSX/environment.plist, on my Lion it is read and used. Just tested it. I actually prefer it over .launchd.conf because I use the RCenvironment preference pane to maintain it.Priority
Agreed, environment.plist works fine on Lion, I also use it. Not necessarily future proof though...!Cacoepy
@Priority and snowcrash09: thanks for pointing that out, I removed that bit about Lion. (FWIW, it had been added by someone else.)Premed
Can't get ~/.launchd.conf to work on 10.6.8 - it doesn't appear to have any effect. Also man page says this file is currently unsupported.Cacoepy
~/.launchd.conf doesn't seem to work on 10.7.3 either and when I look in the man page it says $HOME/.launchd.conf Your launchd configuration file (currently unsupported)Transpontine
/etc/launchd.conf works just fine on 10.8.2, just remember not to put any quotes around the values (e.g. setenv PATH /usr/bin:/usr/local/bin not setenv PATH "/usr/bin:/usr/local/bin")Dampproof
In 10.8 (Mountain Lion), ~/.MacOSX/environment.plist is no longer supported. According to Apple Dev, one must "Change the Info.plist of the .app itself to contain an "LSEnvironment" dictionary with the environment variables you want." For more info, see apple.stackexchange.com/questions/57385/…Jewett
Doing steps 1 and 2 at the end of your post worked for me on 10.8.2. Thanks.Dostie
@uncreative: $HOME/.launchd.conf is still marked unsupported in the 10.8.2 manpage.Refreshment
@Ryan: I am unable to reproduce this working with ~/.launchd.conf or directly with launchd on 10.8.2. At least, neither Terminal.app nor Emacs, when relaunched after making the change, reflect my changes.Refreshment
@DaveAbrahams: Strange. I didn't do anything special. Just copy pasted into a terminal window. Changed the fields and hit enter.Dostie
~/.launchd.conf is still not working as of OS X 10.8.3, and the unsupported status is still mentioned in man launchd.conf (but NOT in man launchctl, where ~/.launchd.conf is mentioned as well).Court
@Premed Great, comprehensive post; could you please update it to note that ~/.launchd.conf is still not supported and doesn't work as of OS X 10.8.3? See man launchd.confCourt
@mklement thanks for the correction. I wanted to verify ~/.launchd.conf's brokenness by myself, but I kept putting it off. I finally got around to doing it, and I added a workaround.Premed
Thanks for the update and for the elegant workaround - comes with a caveat, though (may not be a problem for everyone): tasks run as user launch agents are not guaranteed to execute before other login items, so apps launched as login items may not get to see these definitions. Also worth mentioning: ~/.MacOSX/environment.plist is only supported up to and including 10.7. Finally, /etc/launchd.conf currently doesn't allow you to define values with embedded spaces (I've filed a bug with Apple).Court
Fantastic answer but it doesn't apply any more to Mavericks. An update is needed. See here: apple.stackexchange.com/questions/106355/…Devine
launchd.conf is (now) long deprecated. launchd.plist is (currently, as of Yosemite) the way, the truth, and the light. see man launchd.plist and man launchd.confArk
@Premed By the way, the < should be replaced with &lt;Earwig
Also, launchctl doesn't take input from stdin. Fixed using xargs.Earwig
Note that if you are trying to override $SSH_AUTH_SOCK (when using GPG agent for example), you will need to disable the macOS ssh-agent first, otherwise it will override that variable. Disable System Integrity Protection and run launchctl unload /System/Library/LaunchAgents/com.openssh.ssh-agent.plist to do that (and then enable SIP again, of course).Dampproof
launchctl setenv MYVARIABLE "QUOTE THE STRING" did it for me (macOS Big Sur M1). I also put it in ~/Library/LaunchAgents/environment.plist, but that didnt seem to do it. It worked only after I directly used the launchctl setenv command.Rufinaruford
C
160

There's no need for duplication. You can set environment variables used by launchd (and child processes, i.e. anything you start from Spotlight) using launchctl setenv.

For example, if you want to mirror your current path in launchd after setting it up in .bashrc or wherever:

PATH=whatever:you:want
launchctl setenv PATH $PATH

Environment variables are not automatically updated in running applications. You will need to relaunch applications to get the updated environment variables (although you can just set variables in your shell, e.g. PATH=whatever:you:want; there's no need to relaunch the terminal).

Cruck answered 21/9, 2010 at 1:20 Comment(12)
Looks like the best answer so far, no need for a 3rd party app!Shabby
This doesn't appear to be global: environment variables set this way are local to the user. We still don't have a global mechanism for setting an environment variable.Wyattwyche
@Wyattwyche What do you mean, local to the user? I would expect all processes subsequently started from launchd to be affected.Cruck
@MattCurtis: Environment variables set via launchctl setenv seem to only be visible to the user making the change. If I set a variable as an ordinary user, it's not visible to root (via sudo) and vice-versa.Wyattwyche
@Wyattwyche OK, root has its own launchd - ps aux | grep launchd will show this. Also check man sudo, which documents that sudo (by default) deliberately resets the environment - if you sudo -E it will preserve the environment (including variables you've set with launchctl setenv). Do you have an actual application for this, by the way? If so, does this method work for you?Cruck
@MattCurtis: The main application is whenever a GUI app needs an environment variable, and you want to set it for all users. Most Macs tend to be single-user, so admittedly in practice the non-global problem isn't a problem. (A concrete example is that IntelliJ IDEA (as of 10) needs M2_HOME to be set for maven integration to work properly. For the system maven this means /usr/share/maven. Given that this is system-wide I found it frustrating that I couldn't set it in a likewise manner.)Wyattwyche
@Wyattwyche In that case is /etc/profile not a reasonable solution for you? It will get sourced whenever a new user logs on. Looking after a multiuser system you're probably not wanting to monkey around with environment variables for existing sessions. Otherwise, you may need to identify all launchd processes and write a script - because you're looking at a programming website I'm guessing that's an acceptable solution :-)Cruck
@MattCurtis Sadly /etc/profile is only used for processes launched as children of a Bourne-shell-based process. GUI apps don't generally qualify. In summary we have ways to set environment variables for: a) All processes for a user (launchd or ~/.MacOSX/environment.plist); b) All Bourne-shell-based processes (/etc/profile); c) But not both.Wyattwyche
@Wyattwyche So do you have multiple GUI users logged on to the OSX desktop simultaneously? How does that work? In any case, why can't you use the mechanic from my answer (to reflect command line changes in launchd's PATH, which I believe is what the OP was asking for) to do (c) "both"? Or are you after a no-code solution? In that case maybe you will have more luck asking about this on superuser.Cruck
You need to also add 'launchctl export' at the end to actually export all launchctl setenv variables.Drake
@PeriodicMaintenance I don't think so. "launchctl export" is in the manpage but 10.10 launchctl says "Unrecognized subcommand: export". Besides, the manpage says it exports "for use in a shell eval statement" -- in the example I gave, PATH is already exported, "setenv" will set the variable in launchd's environment, so there's nothing further to export. But I guess -- if the subcommand existed -- you could use eval "$(launchctl export)" to export variables not already exported -- though you could just add "export" when setting the variable (e.g. export ANSWER=42.)Cruck
launchctl config system path $PATH will take care of root launchd. I believe launchctl config user path $PATH would be the preferred way for to set a persistent path for user launchd. (vs setenv).Kami
D
12

I think what the OP is looking for is a simple, windows-like solution.

here ya go:

https://www.macupdate.com/app/mac/14617/rcenvironment

Drogheda answered 2/12, 2010 at 14:23 Comment(4)
Wow, this looks cool. haven't tried it yet but looks like exactly what I needed from the description.Shabby
btw, the original link seems to have broken since I posted it (what's the matter Apple? 301's are expensive?). You can use this link instead: macupdate.com/app/mac/14617/rcenvironmentDrogheda
This is really old. Check out the other answer that mentions osx-env-sync for a modern solution that works even in OS X 10.10 (Yosemite) and up.Housecarl
Please summarize the information from the link in your answer. As we've already seen, links break for a variety of reasons.Sandstrom
F
8

You can read up on linux, which is pretty close to what Mac OS X is. Or you can read up on BSD Unix, which is a little closer. For the most part, the differences between Linux and BSD don't amount to much.

/etc/profile are system environment variables.

~/.profile are user-specific environment variables.

"where should I set my JAVA_HOME variable?"

  • Do you have multiple users? Do they care? Would you mess some other user up by changing a /etc/profile?

Generally, I prefer not to mess with system-wide settings even though I'm the only user. I prefer to edit my local settings.

Faunus answered 2/3, 2009 at 20:13 Comment(0)
H
5

For GUI apps, you'll have to create and edit ~/.MacOSX/environment.plist. More details here. You will need to log out for these to take effect. I'm not sure if they also affect applications launched from Terminal, but I assume they would.

For apps launched from Terminal, you can also edit the ~/.profile file.

Houchens answered 2/3, 2009 at 20:10 Comment(2)
Yes, Terminal will inherit the variables, as will anything launched from Terminal. You can use the RCenvironment preference pane to maintain the variables.Priority
This solution no longer works with some revisions of Mac OS X v10.7. It doesn't with any revision of Mac OS X v10.8 or greater. Instead, see: https://mcmap.net/q/36017/-environment-variables-in-mac-os-xRazorbill
R
4

Synchronize OS X environment variables for command line and GUI applications from a single source with osx-env-sync.

I also posted an answer to a related question here.

Rakes answered 4/9, 2015 at 21:10 Comment(3)
This is fantastic. Suggestion: Put the launchctl unload / launctl load "refresh now" thing into a script. I called it osx-env-sync-now.sh. I modify my .bash_profile and run the little "refresh now" script and continue. I think there are security implications here, so I think some limitations should be made. There must be reasons they turned this capability off in OS X.Housecarl
@WarrenP Done! Check the repo.Rakes
Excellent. This solved a lot of pain for me. One case where this is really useful is for anyone developing with SCALA. Setting SCALA_HOME for both command line scala and GUI scala (such as in netbeans) is a real pain otherwise.Housecarl
S
3

Just open the ~/.profile file, via nano in Terminal and type there :

export PATH=whatever/you/want:$PATH

Save this file (cmd+X and Y). After that please logout/login again or just open a new tab in Terminal and try use your new variable.

PLEASE DON'T forget to add ":$PATH" after whatever/you/want, otherwise you'll erase all paths in PATH variable, which were there before that.

Synecology answered 12/4, 2012 at 9:3 Comment(1)
This only applies to the bash command environment. Variables you set here are not seen by GUI applications.Housecarl
N
3

I wrote a tool to make it easy to manage the environment variables for macOS applications.

https://github.com/yuezk/macenv

You can set the environment variable with macenv set, for example:

macenv set JAVA_HOME /path/to/java/home

Under the hood, it calls launchctl setenv to set the environment variables, saves the environment variables to ~/.launchd.conf at the same time, and registers an auto-start service to load the environment variables when the OS restarts.

Nonresistance answered 7/1, 2022 at 8:38 Comment(1)
Nice tool! I think ~/.macenv in example should be replaced by macenv, to match the latest installation instructions.Orbadiah
C
1

I looked into this and found that when you start Eclipse using the icon which comes with the installer, the path for graphviz /usr/local/bin is not included, whereas it is for terminal windows. The reason is explained above in LaC's post above.

The way to include the path is to simply start Eclipse from a terminal window, instead of clicking the icon. In my case:

cd Eclipse.app
cd Contents
cd MacOs
./eclipse

this launches Eclipse and includes the inherited path variables

Curcio answered 1/2 at 16:19 Comment(0)
T
-1

If you want to change environment variables permanently on macOS, set them in /etc/paths. Note, this file is read-only by default, so you'll have to chmod for write permissions.

Tyrannize answered 29/11, 2015 at 21:10 Comment(4)
This doesn't work for me. I have /usr/bin/local in that file, even without modifying the file, it was that way by default, and yet my GUI apps see only /usr/bin:/bin:/usr/sbin:/sbin. I rebooted many times.Millardmillboard
@m_gol What do you get when you run cat /etc/paths/?Tyrannize
/usr/local/bin, /usr/bin, /bin, /usr/sbin, /sbin, in separate lines. And yet SourceTree sees all of them except the first.Millardmillboard
You might want to edit to "If you want to change the default path in macOS". This doesn't have anything to do with the more general problem of environment variables.Kuntz
G
-3

For 2020 Mac OS X Catalina users:

Forget about other useless answers, here only two steps needed:

  1. Create a file with the naming convention: priority-appname. Then copy-paste the path you want to add to PATH.

    E.g. 80-vscode with content /Applications/Visual Studio Code.app/Contents/Resources/app/bin/ in my case.

  2. Move that file to /etc/paths.d/. Don't forget to open a new tab(new session) in the Terminal and type echo $PATH to check that your path is added!

Notice: this method only appends your path to PATH.

Gotha answered 18/4, 2020 at 4:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.