在OS X Yosemite/El Capitan/macOS Sierra/Mojave中,通过launchd.conf设置环境变量不再起作用?

203
看起来launchd.conf不再加载我的环境变量。还有其他人注意到这个问题吗?
是否有另一种解决方案可以永久设置环境变量?

这个回答解决了你的问题吗?在OS X上设置环境变量 - undefined
9个回答

182
~/Library/LaunchAgents/中创建一个environment.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>my.startup</string>
  <key>ProgramArguments</key>
  <array>
    <string>sh</string>
    <string>-c</string>
    <string>
    launchctl setenv PRODUCTS_PATH /Users/mortimer/Projects/my_products
    launchctl setenv ANDROID_NDK_HOME /Applications/android-ndk
    launchctl setenv PATH $PATH:/Applications/gradle/bin
    </string>

  </array>
  <key>RunAtLoad</key>
  <true/>
</dict>
</plist>

您可以在<string></string>块中添加许多launchctl命令.

plist会在系统重新启动后激活。 您也可以使用launchctl load ~/Library/LaunchAgents/environment.plist立即启动它。

[编辑]

相同的解决方案在El Capitan上也适用。

Xcode 7.0+默认不评估环境变量。 可以使用此命令启用旧行为:

defaults write com.apple.dt.Xcode UseSanitizedBuildSystemEnvironment -bool NO

[编辑]

有几种情况下这种方法不太可行。如果计算机重新启动并选择了“重新打开登录时的窗口”,则重新打开的窗口可能无法看到变量(可能是在运行代理之前打开的)。 此外,如果通过ssh登录,则不会设置变量(因此您需要在~/.bash_profile中设置它们)。 最后,在El Capitan和Sierra上,PATH似乎无法正常工作。 必须通过'launchctl config user path ...'和/etc/paths来设置它。


22
无需重新启动!您可以执行“launchctl start environment.plist”并重新启动所需的应用程序,以获取新的环境变量 ;) - hasvn
7
明白了:为了不重启就能工作,应该使用"launchctl load environment.plist"而不是_start_. - Dave Hartnoll
4
啊,没错。没有什么比整个互联网上只出现9次的晦涩配置设置更让人头疼的了(谷歌搜索“UseSanitizedBuildSystemEnvironment”)。 - Ohad Schneider
2
同样适用于Sierra操作系统。 - Shwouchk
5
sudo launchctl config user path $PATH 可用于 Mojave。 - Ivan Romanov
显示剩余11条评论

66
[原始回答]: 您仍然可以使用launchctl setenv variablename value设置变量,以便所有应用程序都可以使用(通过Dock或Spotlight启动的图形应用程序,以及通过终端启动的应用程序)。

显然,您不希望每次登录时都这样做。

[编辑]: 为了避免这种情况,请启动AppleScript Editor,输入以下命令:

do shell script "launchctl setenv variablename value"

(如果您想设置多个变量,请使用多行)

现在保存 (+s) 为 文件格式:应用程序。最后打开 系统偏好设置用户与群组登录项 并添加你的新应用程序。

[原始回答]: 为解决这个问题,将您希望定义的所有变量放入一个短shell脚本中,然后查看此有关如何在MacOS登录时运行脚本的先前回答。这样,当用户登录时,脚本将被调用。

[编辑]: 两种解决方案都不完美,因为变量只会针对该特定用户进行设置,但我希望/猜测这可能是您需要的全部。

如果您有多个用户,您可以手动为每个用户设置一个登录项,或者将com.user.loginscript.plist的副本放置在他们各自的本地Library/LaunchAgents目录中,指向同一个shell脚本。

无可否认,这些解决方法都不像/etc/launchd.conf那样方便。

[进一步编辑]: 下面的用户提到这对他没有起作用。然而,我已经在多台Yosemite机器上进行了测试,并且它对我有用。如果您遇到问题,请记住您需要重新启动应用程序才能使其生效。此外,如果您通过~/.profile~/.bash_profile在终端中设置变量,则它们将覆盖通过launchctl setenv从shell启动的应用程序设置的内容。


5
据我所知,这种技术的一个缺点是,变量在登录后启动的任何其他应用程序中都不会被设置。例如,如果您打开终端,变量将被设置,但是如果您注销并重新登录,使终端自动重启,该变量将被取消设置... - JasonD
这对我有效,但是你知道如何将环境变量添加到sudo吗? - etiennenoel
2
这个方法通常是有效的,但在Yosemite(至少是10.10.0和10.10.1版本)中存在一个漏洞,即以这种方式设置$PATH无效。苹果公司已经意识到了这个问题。目前(截止到10.10.1版本),还没有已知的方法可以为GUI应用程序设置系统范围的$PATH。 - TJ Luoma
3
在使用以上提到的任一方法并重新启动您的笔记本电脑后,确保显式地重新打开应用程序(例如iTerm、终端、Eclipse、IDEA或您正在使用的其他应用程序)。如果您不显式地重新启动它们,并且在重启OSx时选中了“重新登录时重新启动窗口”复选框(这是默认设置),那么这些程序将无法读取新的环境变量。 - Ran
  1. 运行 launchctl setenv 变量名 值
  2. 然后重启 shell - 对我有用
- Chicago
显示剩余2条评论

21

使用三个文件和两个命令可以在 Mac OS X 10.10 Yosemite 上设置环境变量。

包含环境变量定义的主文件:

$ ls -la /etc/environment 
-r-xr-xr-x  1 root  wheel  369 Oct 21 04:42 /etc/environment
$ cat /etc/environment
#!/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"

为用户应用程序(终端、IDE等)加载环境变量的服务定义:

$ ls -la /Library/LaunchAgents/environment.user.plist
-rw-------  1 root  wheel  504 Oct 21 04:37 /Library/LaunchAgents/environment.user.plist
$ sudo cat /Library/LaunchAgents/environment.user.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>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>

相同的服务定义适用于根用户应用程序:

$ ls -la /Library/LaunchDaemons/environment.plist
-rw-------  1 root  wheel  499 Oct 21 04:38 /Library/LaunchDaemons/environment.plist
$ sudo cat /Library/LaunchDaemons/environment.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>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>

最后,我们应该注册这些服务:

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

我们获得了什么:

  1. 声明系统环境变量的唯一位置:/etc/environment
  2. 在修改/etc/environment文件后,自动更新环境变量-只需重新启动您的应用程序即可

问题 / 问题:

为了让您的环境变量在系统重启后被应用程序正确获取,您需要:

  • 要么登录两次:登录= >注销= >登录
  • 要么手动关闭和重新打开应该获取环境变量的应用程序
  • 或者不使用"重新打开登录时的窗口"功能。

这是因为Apple拒绝明确加载服务的顺序,因此环境变量与“重新打开队列”的处理同时注册。

但实际上,我每年只重启几次系统(对于大型更新),所以这不是什么大问题。


很好的想法。我已经尝试过了,它适用于大多数环境变量(如JAVA_HOME),但不适用于PATH变量(请参见我的问题)。 - halloleo
4
PATH应该在/etc/paths文件中设置。只需将您的自定义路径添加到此文件的末尾即可。 - ursa
我喜欢上面的方法,但是有一个奇怪的问题需要解决。在重新启动后,genet VARNAME返回正确的值,但echo $VARNAME却没有返回任何内容。这可能是什么原因?我也在http://stackoverflow.com/questions/27045137/strange-behaviour-for-setenv-getenv-in-os-x-yosemite上发布了这个问题,希望这里的任何人都有想法。 - ctp
我想插一句话并提到,我尝试了各种方法来为OSX Yosemite设置自定义环境变量,而这个解决方案似乎是最好的。对我来说,这些解决方案都不太“干净”,但这是最简单的,并且适用于我所有的程序。 - CoBrA2168
在我的问题中,我概述了如何使用上述方法来重复使用我的shell导出,以便只有一个真正的环境变量来源,即在我的情况下是.zshrc文件。我认为这是上述方法的扩展。 - user816328
显示剩余2条评论

10

引用自

Apple开发者关系 2014年10月10日 下午9:12

经过深思熟虑,工程师已删除此功能。 文件/etc/launchd.conf因安全原因被有意删除。 作为解决方法,您可以在启动期间以root身份运行launchctl limit,可能需要从一个LaunchDaemon中运行。 (...)

解决方案:

使用bash脚本将代码放入/Library/LaunchDaemons/com.apple.launchd.limit.plist中:

#!/bin/bash

echo '<?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>eicar</string>
        <key>ProgramArguments</key>
        <array>
                <string>/bin/launchctl</string>
                <string>limit</string>
                <string>core</string>
                <string>unlimited</string>
        </array>
        <key>RunAtLoad</key>
        <true/>
        <key>ServiceIPC</key>
        <false/>
</dict>
</plist>' | sudo tee /Library/LaunchDaemons/com.apple.launchd.limit.plist

1
你能再解释一下吗?我看不出“解决问题”与最初的问题有什么关系! - Nick Hingston
1
不是楼主,但我认为这里的要点是:将此 plist 放入 /Library/LaunchDaemons 中,而不是告诉 launchctl 运行 limit 命令,告诉它运行带有 PATH 和路径字符串作为参数的 setenv 命令。launchd 应该会在启动时自动捕获它,并几乎立即进行自我修改。 - Laird Nelson
6
XML内容未完全复制,doctype行应为 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> - UloPe
9
@aax,那个plist的哪一部分实际上设置了环境变量? - HairOfTheDog

5
以下是恢复旧行为的命令:
# create a script that calls launchctl iterating through /etc/launchd.conf
echo '#!/bin/sh

while read line || [[ -n $line ]] ; do launchctl $line ; done < /etc/launchd.conf;
' > /usr/local/bin/launchd.conf.sh

# make it executable
chmod +x /usr/local/bin/launchd.conf.sh

# launch the script at startup
echo '<?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>launchd.conf</string>
  <key>ProgramArguments</key>
  <array>
    <string>sh</string>
    <string>-c</string>
    <string>/usr/local/bin/launchd.conf.sh</string>
  </array>
  <key>RunAtLoad</key>
  <true/>
</dict>
</plist>
' > /Library/LaunchAgents/launchd.conf.plist

现在你可以在/etc/launchd.conf中指定命令,例如setenv JAVA_HOME /Library/Java/Home

已在El Capitan上检查过。


这是一个不错的解决方案。我认为 setenv 命令只适用于调用者的上下文,所以这对于 LaunchAgents 可以起作用,而对于 LaunchDaemons 需要一个单独的文件。 - SpinUp __ A Davis

2

以下是对我有效的方法(受 aax 感谢启发):

将以下内容粘贴到/Library/LaunchDaemons/com.apple.launchd.limit.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>eicar</string>
  <key>ProgramArguments</key>
  <array>
    <string>/bin/launchctl</string>
    <string>limit</string>
    <string>maxfiles</string>
    <string>16384</string>
    <string>16384</string>
  </array>
  <key>RunAtLoad</key>
  <true/>
  <key>ServiceIPC</key>
  <false/>
</dict>
</plist>

如果您需要逐步操作:
  • 启动终端
  • 输入sudo su,然后输入密码以root用户身份登录
  • 输入vi /Library/LaunchDaemons/com.apple.launchd.limit.plist
  • 进入vi编辑器后,按下i键进入插入模式,然后粘贴上面的代码内容(⌘+v)。这将强制限制每个进程的文件数为16384个,总文件数为16384个
  • 保存文件并使用esc:wq退出
  • 重新启动系统,并使用命令launchctl limit检查是否工作正常

希望这对您有所帮助。


11
这个解决方案与设置环境变量有什么关系? - HairOfTheDog

2
你可以尝试使用https://github.com/ersiner/osx-env-sync。它可以从一个单一来源处理命令行和GUI应用程序,并与最新版本的OS X(Yosemite)兼容。你可以使用路径替换和其他shell技巧,因为你编写的是普通的bash脚本,首先由bash源码解析。没有限制..(查看osx-env-sync文档,你会明白它是如何实现的。)我在这里回答了类似的问题(链接),你可以找到更多信息。

-4
解决方案是将您的变量添加到/etc/profile中。然后一切都会按预期工作!当然,您必须以root用户身份使用sudo nano /etc/profile进行操作。如果您以其他方式编辑它,则系统会抱怨/etc/profile已损坏,即使您更改了根权限也是如此。

7
将环境变量添加到配置文件中非常低效,因为它只影响 shell 进程。 - UloPe

-6
我按照以下方式在~/.bash_profile中添加了变量。添加完成后,请重新启动/注销并重新登录。
export M2_HOME=/Users/robin/softwares/apache-maven-3.2.3
export ANT_HOME=/Users/robin/softwares/apache-ant-1.9.4
launchctl setenv M2_HOME $M2_HOME
launchctl setenv ANT_HOME $ANT_HOME
export PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/Users/robin/softwares/apache-maven-3.2.3/bin:/Users/robin/softwares/apache-ant-1.9.4/bin
launchctl setenv PATH $PATH

注意:您可以在不重新启动或注销登录的情况下应用这些更改;

source ~/.bash_profile

1
请注意,您无需注销并重新登录。只需使用source命令,即source .bash_profile。 - Michael
2
此外,这种方法的问题在于您仍然需要在环境变量可用之前打开终端。最好按照第一个答案中的方法进行操作,这样它们就可以在不必打开终端的情况下使用。 - Michael
1
这在通过SpotLight加载的应用程序上不起作用。https://dev59.com/3nVC5IYBdhLWcg3w9GHM - Govinnage Rasika Perera
1
使用bash配置文件的帮助有限,因为它假定您始终将bash作为尝试影响其环境的进程的祖先。Spotlight、Finder、Emacs、Xcode、cronjobs、launchd代理、任何IDE、源代码控制浏览器等等都不会有bash作为祖先。唯一可以跨越这些的进程是launchd。 - Ben Hyde

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接