虽说Yosemite去年就出了,但是前两天才把黑苹果升级到Yosemite。这个行为多少有点作死,因为10.10目前的评价相当低,坚持使用Mavericks的人不在少数。而且Yosemite的黑苹果也显然不如Mavericks成熟。

不管怎么说升级了。但是很遗憾的发现我前年还在用ML时候的这篇文章里用于修改Mac下GUI程序的环境变量的方法均已失效或部分失效。Yosemite下Launchd不再读取/etc/launchd.conf并执行了。

再三寻找终于在Stack Overflow下找到了比较完整的答案。做一下记录和翻译供后来人参考。

首先说下之前这篇文章里的方法。

1.launchctl setenv指令其实仍然有效。但是Yosemite下“看起来”似乎无效了。后面我的补充会对此做出解释。
2./etc/launchd.conf文件完全失效了。该文件在Yosemite下完全被抛弃了,不会被读取。所以需要通过比较正规的途径注册Launchd服务来完成设置环境变量的操作。实际上这样的做法也是相当有优势的。

下面是Stack Overflow上ursa的答案。


Stack Overflow答案原文

在Mac OS X 10.10 Yosemite下可以使用3个文件配合2条命令来修改环境变量。

包含有环境变量定义的主要文件是/etc/environment。权限应设置为555。内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/sh
set -e
syslog -s -l warn "Set environment variables with /etc/environment $(whoami) - start"
launchctl setenv JAVA_HOME /usr/local/jdk1.7
launchctl setenv MAVEN_HOME /opt/local/share/java/maven3
if [ -x /usr/libexec/path_helper ]; then
export PATH=""
eval `/usr/libexec/path_helper -s`
launchctl setenv PATH $PATH
fi
osascript -e 'tell app "Dock" to quit'
syslog -s -l warn "Set environment variables with /etc/environment $(whoami) - complete"

为用户程序(GUI程序)加载环境变量的服务定义文件为/Library/LaunchAgents/environment.user.plist。权限为600。内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?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>environment.user</string>
<key>ProgramArguments</key>
<array>
<string>/etc/environment</string>
</array>
<key>KeepAlive</key>
<false/>
<key>RunAtLoad</key>
<true/>
<key>WatchPaths</key>
<array>
<string>/etc/environment</string>
</array>
</dict>
</plist>

为root权限用户程序的服务定义文件为/Library/LaunchDaemons/environment.plist(注意此处是Daemons)。权限应为600。内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?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>environment</string>
<key>ProgramArguments</key>
<array>
<string>/etc/environment</string>
</array>
<key>KeepAlive</key>
<false/>
<key>RunAtLoad</key>
<true/>
<key>WatchPaths</key>
<array>
<string>/etc/environment</string>
</array>
</dict>
</plist>

最后执行下面的命令来加载服务:

1
2
$ launchctl load -w /Library/LaunchAgents/environment.user.plist
$ sudo launchctl load -w /Library/LaunchDaemons/environment.plist

最终效果:

1.在且仅在/etc/environment文件中定义系统环境变量。
2.在修改了该文件之后环境变量会自动更新——只需要重新启动你的程序。

少量问题:

系统重启之后,为了让你的环境变量的设置正确的生效,你需要:登入两次:登入,注销再登入;或者手动关闭并重新开启应用程序;或者不要使用“登录时重新开启窗口”的功能。
这是因为苹果在加载服务时并不严格按照顺序加载,所以环境变量的加载和“重新打开窗口”的过程是同步进行的。

但是实际上OSX系统一年重启不了几次,所以不是大问题。


在Stack Overflow的答案上进行几点补充:

1./etc/environment是定义环境变量的关键文件。不过从中可以看出来$PATH的定义不在此处,而是通过调用path_helper生成的。path_helper根据/etc/paths文件和/etc/paths.d/目录来生成$PATH和$MAN_PATH环境变量。通常在运行Terminal的时候被调用来给命令行session构建环境变量。

所以要修改$PATH不要在/etc/environment里改,而是在/etc/paths里添加。

2.以前文章中launchctl setenv VARNAME VARVALUE的命令其实是有用的,包括对$PATH。但是跟从Terminal session里启动程序会继承Terminal session的环境变量类似,在Yosemite下从Launchpad或者Dock下启动的应用程序也会继承Dock的环境变量。但是不同于Terminal session,Dock的环境变量没法在运行的时候改。所以其实使用launchctl setenv的命令之后需要终止Dock并让其自动重启来让环境变量在Dock里生效,从而让之后在Dock里启动的程序也能继承同样的环境变量。

/etc/environment文件里的oascript命令就是为了实现这样的效果。