How to set environment variables on MacOS using a GUI?

I'm trying to provide instructions to several people on how to set an environment variable in MacOS.

I want the variable to be permanent, not require reboot, and it must work for all gui applications.

Unfortunately, any solution that requires vim or even nano isn't going to fly.

How exactly can this be done only using a gui application? (And the simpler the better)


Solution 1:

I have set up a way to do this, with documentation text files. However, it does require use of nano if the user wants to decide what environment variables to set. If you supply that, then the user doesn't have to use nano. BUT, the process is tricky with lots of gotchas to install it, so even then it is probably too much for your users. But I'd like to make it available to anyone that wants it. Since I can't attach a file here, I guess I'll put all the file contents into code blocks below. The method used is to create a .plist file that MacOS loads at startup, and that file has bash code that reads a text file provided in the user's home directory containing environment variables to be set.

Here is the contents of the environment.plist file:

<?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>my.startup</string>
    <key>ProgramArguments</key>
    <array>
        <string>/bin/bash</string>
        <string>-c</string>
        <string>while read LINE; do NAME=${LINE%%=*}; VALUE=${LINE#*=}; eval launchctl setenv $NAME &quot;$VALUE&quot;; done &lt;~/environment.txt; echo &quot;environment.plist ran on $(date)&quot; &gt;&gt; ~/environment.plist.log</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
</dict>
</plist>

Here is a sample environment.txt file to be placed in the user's home directory to set some environment variables:

LOCAL_ROOT=/Users/$USER/archive
SOFTWARE_ROOT=$LOCAL_ROOT/PKGS
REPO_DIR=$SOFTWARE_ROOT/repositories
RSOURCEPATH=$REPO_DIR/rscripts/libraries

Here is my environment.readme.txt file to accompany environment.txt in the user's home directory:

This file accompanies file environment.txt.

PLACE GLOBAL ENVIRONMENT VARIABLES IN ~/environment.txt   !!!!

Full instructions describing environment.txt can be found in the file:
    environment.plist.readme.txt
which should be in the directory ~/Library/LaunchAgents

Edit environment.txt to set the environment variables that you want.  You can
edit the file with any text editor, e.g. nano:
    nano ~/environment.txt
In nano, use the arrow keys to move around, the backspace key to delete text,
and use control/x followed by a Y and enter to exit and save the changes. Also,
follow the steps in environment.plist.readme.txt to check the file for correct
line endings and fix them if necessary.

PLACE GLOBAL ENVIRONMENT VARIABLES IN ~/environment.txt   !!!!
THEY CAN USE OTHER PRE-EXISTING ENVIRONMENT VARIABLE VALUES IN THEM !!!!
FOR EXAMPLE: XXX=/Users/$USER/xxx

LOG OUT AND LOG BACK IN FOR THE CHANGES TO TAKE EFFECT!

Finally, here is the environment.plist.readme.txt file with the detailed instructions for use:

PLACE GLOBAL ENVIRONMENT VARIABLES IN ~/environment.txt   !!!!

This accompanies file environment.plist (for Mac computers running MacOS) that
sets global environment variables so that they can be seen by apps started via a
mouse click on an icon.  (Environment variables set in a shell profile file
cannot be seen by apps started that way).

The environment.plist file should load and run automatically whenever you boot
your Mac.

To use the environment.plist file, while logged in as your username and not as
root user, copy this file and environment.plist to your directory named:
    ~/Library/LaunchAgents
Then set the correct file permissions with:
    chmod u+rw,g+r,o+r ~/Library/LaunchAgents/environment.plist
Also, make sure the file hasn't been quarantined by an anti-virus program or
had extended attributes attached to it that might interfere with its loading,
by using the command:
    ls -l
You should see permissions for the file as -rw-r--r-- and if an @ occurs after
this, it means the file has extended attributes, which you can view with:
    xattr ~/Library/LaunchAgents/environment.plist
The attribute com.apple.quarantine will definitely prevent the file from loading
and other attributes might also block it.  You can remove an attribute with:
    xattr -d <attribute name> ~/Library/LaunchAgents/environment.plist
Then test the .plist file for correctness with the command:
    plutil ~/Library/LaunchAgents/environment.plist

Next, copy environment.txt and environment.readme.txt to your ~ (home) directory
and edit environment.txt to set the global environment variables you desire.
(The sample environment.txt file has some sample settings). You can use existing
environment variables such as USER as part of your environment variable
definitions in environment.txt, e.g. "XXX=/Users/$USER/xxx", and you can use
your new environment variables in subsequent environment variable definitions in
the file.

For the next step, you need to know your user id uid number, which you can find
with this command:
    id
Typically on a Mac the uid often is 501.  Using the uid, test the .plist and
.txt files with these commands:
    launchctl bootout gui/501 ~/Library/LaunchAgents/environment.plist
    launchctl enable user/501/~/Library/LaunchAgents/environment.plist
    launchctl bootstrap gui/501 ~/Library/LaunchAgents/environment.plist

Each time environment.plist runs, it writes a line at the end of a log file
showing the date it ran.  Examine that file to see if it has a new entry, using
this command:
    cat ~/environment.plist.log
Each time you log in, and each time you run the three launchctl commands above,
a new line should be added to the log file, so you should see one line in the
file after running the above launchctl commands.

At this point, logging out and back in should cause the environment.plist file
to run and read the environment.txt file and assign those environment variables.
You should see them in any program you start yourself, but they won't show up in
any program started by MacOS automatically at login (and I don't know how to fix
that problem).

If everything works well, skip the remainder of this document, but if not, read
on and you might be able to fix the problem.

It is important that the environment.txt file have only a newline (\n) as line
end, not \r\n or only \r. If you created it with the nano editor, it should be
okay. To find out what the line endings are, you can use this command:
    hexdump ~/environment.txt | less
Look through the hex bytes to find 0a (\n), 0d (\r), or 0d followed by 0a. It
should occur at the end of each line (each hex byte XX corresponds to one
character in the file) and particularly after the last line. Use the space bar
to scroll through the bytes and q to quit out of the 'less' program. If you find
only a 0d and no 0a, fix the file like this:
    tr '\r' '\n' ~/environment.txt >env.txt
    mv env.txt environment.txt
If you find 0d followed by 0a, fix the file like this:
    tr -d '\r' ~/environment.txt >env.txt
    mv env.txt environment.txt

If the log file ~/environment.plist.log doesn't exist or doesn't have a new line
at the end of the file showing the date you ran the launchctl commands above,
then something is wrong, which is frequently the case. You need to go back and
check all the above steps and recheck the file contents, owner, and permissions
carefully and try the launchctl commands a second time.  If it still isn't
adding a new entry to the log file when you run those commands, then restarting
your computer might fix the problem.  Do that and try again.  Otherwise, I don't
know what else to try.

If the log file looks okay, test to see if your environment variables have been
set by exiting the Terminal application completely, then restarting it, and
using this command:
    echo $<ENVIRONMENT VARIABLE NAME>
Use the name of one of your environment variables, e.g. 'echo $LOCAL_ROOT'
You should see the VALUE you assigned to that environment variable printed out.
The value will be empty if you have not exited and restarted your terminal
console app after running the launchctl commands.

Next, log out and log back in to your computer and go into the Terminal app and
do the above 'echo' command again. If it doesn't print out the expected
environment variable value, this is probably because Terminal was started by
MacOS automatically when you logged in.  Programs that start automatically will
not have access to the environment variables.  Only programs that you start
yourself will have access.  Try exiting the terminal app and restarting it, then
check the environment variable again.  If it still isn't okay, run through
everything one more time to see if you can get it working, and if not, I don't
have any other ideas to recommend.

Finally, once you see that the 'echo' command shows an environment variable
value, double-check that the variable is accessible from an app started with a
mouse-clicked-icon. If you have RStudio or R installed, start that app with the
mouse. Then, at the console prompt, type the command:
    Sys.getenv("<ENVIRONMENT VARIABLE NAME>")
It should show the value of the environment variable with that name.  E.g.:
    Sys.getenv("LOCAL_ROOT")

CAUTION: If you allow MacOS to restore your apps and windows when you log in, or
if you specify certain apps to be started automatically at login using System
Preferences, Users and Groups, Login Items, those automatically started apps
will not see the new environment variables.  The solution is to close and
re-open any app that was started automatically at login and needs to use those
environment variables.

Theory of operation: When a user logs in, MacOS locates .plist files in folder
~/Library/LaunchAgents and parses them and executes commands in them. The
accompanying plist file, environment.plist, has commands to run a bash shell
subcommand that reads file ~/environment.txt and parses it as a series of
NAME=VALUE lines, then calls 'launchctl setenv' to set NAME as a global
environment variable with value VALUE, and finally, it writes the date to the
end of the log file. All that is accomplished with one long BASH command in the
.plist file, shown here broken into five lines but present in a single LONG line
in the .plist file:
    while read LINE; do NAME=${LINE%%=*}; VALUE=${LINE#*=};
    eval launchctl setenv $NAME &quot;$VALUE&quot;;
    eval export $NAME=&quot;$VALUE&quot;;
    done &lt;~/environment.txt;
    echo &quot;environment.plist ran on $(date)&quot; &gt;&gt; ~/environment.plist.log

Note that semicolons separate BASH commands on one line, &quot; is translated
into the character " (quote), and &lt; is translated into the character < (less
than), and &gt; is translated into the character > (greater than) by the .plist
parser.  The .plist file must have permissions u+rw,g+r,o+r and be owned by the
home directory user and not have a quarantine extended attribute in order for it
to be loaded by MacOS automatically when you log in.