This method uses launchctl to manage environment variables for programs invoked directly from Finder. See the launchctl man page, especially the section LEGACY SUBCOMMANDS. It’s not entirely accurate, but that’s not unusual. The critical subcommands are getenv, setenv, and unsetenv. The man page indicates that the export subcommand is available; it is not.
As far as I know, the procedure outlined below is still valid for Big Sur, but the question is still open. I would not be surprised to learn that the behaviour of launchctl has changed in ways which invalidate some of this discussion. If you find any problems, please let me know in comments.
As explained in a previous post, the now-recommended method of system variable setting is to use the setenv subcommand of launchctl, like so:
launchctl setenv M2 /usr/share/maven/bin
The strings defined in setenv statements like the above, must be string literals. There is no means of resolving variable content based on other variables. For example, you cannot define
setenv TMPDIR $HOME/tmp
Here’s the modified code in .profile to set both launchctl and the bash environment variables.
Create the following functions in .profile.
export LAUNCHCTL_ENV_VARS="$HOME/.launchctl_env_vars"
if [ -f $LAUNCHCTL_ENV_VARS ] ; then rm $LAUNCHCTL_ENV_VARS; fi
set_launchctl_env () {
eval launchctl setenv "$1" \""$2"\"
echo launchctl setenv "$1" \""$2"\" >>$LAUNCHCTL_ENV_VARS
}
unset_launchctl_env () {
eval launchctl unsetenv "$1"
echo launchctl unsetenv "$1" >>$LAUNCHCTL_ENV_VARS
}
set_env_var () {
eval export $1=\""$2"\"
set_launchctl_env "$1" "$2"
}
unset_env_var () {
unset $1
unset_launchctl_env "$1"
}
You may then use the function set_env_var to set both bash and launchctl entries. For example,
set_env_var M2_HOME "/usr/share/maven"
set_env_var M2 "$M2_HOME/bin"
set_env_var HTML_TIDY "$HOME/.tidy"
N.B. The launchd environment variables are distinct from the shell environment variables. Hence the procedure set_env_var sets them both. Note that the environment variables will immediately be available to any shell scripts or Terminal invocations, and the launchd variables should be immediately available to any applications launched from the desktop. (I’m trying to confirm this for Sonoma.)
To get the environment re-established across a login, the launchctl startup features have to be invoked. This is where the file ~/.launchctl_env_vars comes in. Notice that in the .profile code above, we have been executing a launchctl setenv command, and immediately echoing that same command to ~/.launchctl_env_vars.
When the system starts, the launchd process finds system daemon processes to launch from /System/Library/LaunchDaemons & /Library/LaunchDaemons. When a user logs in, launchd looks for user agents to start up in /System/Library/LaunchAgents, /Library/LaunchAgents & ~user/Library/LaunchAgents. While the first two are system-owned directories, the third is owned by the individual user.
The executable: profile2launchctl
#!/bin/sh
#
cmd_list="$HOME/.launchctl_env_vars"
SLEEP_TIME=10
# Uncomment following to echo launchctl commands to stdout
# Key StandardOutPath will have to be set in the plist file
# (pbw.plist2launchctl) for output to be captured.
#ECHO_TO_STDOUT=true
#
one_cmd () {
eval "$@"
}
#
[[ -n "$ECHO_TO_STDOUT" ]] && cat "$cmd_list"
cat "$cmd_list" | while read line; do one_cmd $line; done
[[ -n "$ECHO_TO_STDOUT" ]] && echo Sleeping in profile2launchctl
#
[[ -n "$SLEEP_TIME" ]] && sleep "$SLEEP_TIME"
Notes:
The required sequence of launchctl setenv is in the cmd_list file. The commands are simply read one line at a time, and each line is handed to eval for execution.
Permissions & Location:
To stay on the safe side, give the file -rwxr-xr-x permissions. The locations is as specified in the plist file, which must be edited to reflect this location, and the label you apply. See following description.
The LaunchAgent file: pbw.profile2launchctl
<? 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>pbw.profile2launchctl</string>
<key>KeepAlive</key>
<false/>
<key>Program</key>
<string>{location you choose}/profile2launchctl</string>
<key>RunAtLoad</key>
<true/>
<key>UserName<key>
<string>pbw</string>
</dict>
</plist>
The UserName field will, of course, be set to your own login name. Likewise, the pbw prefix in the Label can be replaced with any suitable identifier, and the log file (see following) directed to a suitable location.
If you need to debug the plist file, add the following lines.
<key>StandardOutPath</key>
<string>/tmp/plist.log</string>
<key>Debug</key>
<true/>