001-macos配置环境变量

对,你的痛点很常见:每加一条变量要改好几处。给你一套「单一真源」方案——只维护一个 .env 文件,其它地方固定不变,同时满足:

下面一次性配好,以后只改 .env


一次性安装(只做一遍)

1) 建一个统一的变量文件

mkdir -p ~/.config/env
nano ~/.config/env/global.env

写入你要的变量(支持注释,等号两边别留空格):

# 全局环境变量(示例)
DASHSCOPE_API_KEY=sk-xxxx
JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-21.jdk/Contents/Home

2) 让 Zsh 永远自动读它(给所有终端)

把这一段追加到 ~/.zshenv(只做一次):

# Load global envs (single source of truth)
if [ -f "$HOME/.config/env/global.env" ]; then
  # shellcheck disable=SC2046
  export $(grep -v '^\s*#' "$HOME/.config/env/global.env" | grep -v '^\s*

这样**所有 zsh 终端**都会加载 global.env(.zshenv 在 zsh 启动时加载,比 .zprofile/.zshrc 更早)。关于各 zsh 文件职责的差异可参考 Apple/社区讨论。

  

> 立刻生效:

source ~/.zshenv


## **3) 让 GUI 应用也吃到这些变量(通过 LaunchAgent)**

  

做一个**登录时自动执行**的小脚本,把 global.env 注入到 GUI 会话的 launchd 环境里(等同 “系统 GUI 会话环境变量”):

  

### **(a) 注入脚本**

mkdir -p ~/.local/bin
nano ~/.local/bin/load-global-env-to-launchd.sh


写入:

#!/bin/zsh
ENV_FILE="HOME/.config/env/global.env"[f"ENV_FILE" ] || exit 0

逐行读取 KEY=VAL(忽略空行与注释)

while IFS='=' read -r key value; do
[[ -z "key"z"key" ]] && continue

去掉可能的引号

value="{value%\"}"; value="{value#"}"
value="{value%\'}"; value="{value#'}"
/bin/launchctl setenv "key""value"
done < "$ENV_FILE"


并赋权:

chmod +x ~/.local/bin/load-global-env-to-launchd.sh


> 为什么这样做?因为 GUI 应用**不会继承你的 shell 文件**,而是继承 **launchd** 的会话环境。用 launchctl setenv 注入到登录会话,就能让 **IDEA、VSCode、Chrome 等**都能读取这些变量。官方与社区都推荐用 **LaunchAgent + launchctl** 实现 GUI 级环境变量,且可跨重启持久生效。    

  

### **(b) LaunchAgent(登录时自动执行脚本)**

mkdir -p ~/Library/LaunchAgents
nano ~/Library/LaunchAgents/com.user.globalenv.plist


写入:

Label com.user.globalenv ProgramArguments /bin/zsh -c $HOME/.local/bin/load-global-env-to-launchd.sh RunAtLoad ```

加载(或直接重启后自动加载):

launchctl load ~/Library/LaunchAgents/com.user.globalenv.plist

说明:早年 ~/.MacOSX/environment.plist 与 /etc/launchd.conf 等方式已被废弃/不再生效,用 LaunchAgent 是现在通行做法。


以后使用(只改一个地方)

新增 / 修改变量 → 只编辑这一个文件:

nano ~/.config/env/global.env

保存后让 GUI 会话即刻更新(无需注销):

~/.local/bin/load-global-env-to-launchd.sh

终端会话里也会在新开窗口时自动加载;若要当前终端马上生效:

source ~/.zshenv

验证:


常见问答


需要的话,我可以把以上步骤打包成一键初始化脚本(创建 global.env、脚本、plist 并自动加载)。你只要粘贴运行即可。 | xargs -I{} echo {})
fi


这样**所有 zsh 终端**都会加载 global.env(.zshenv 在 zsh 启动时加载,比 .zprofile/.zshrc 更早)。关于各 zsh 文件职责的差异可参考 Apple/社区讨论。

  

> 立刻生效:

{{CODE_BLOCK_3}}

## **3) 让 GUI 应用也吃到这些变量(通过 LaunchAgent)**

  

做一个**登录时自动执行**的小脚本,把 global.env 注入到 GUI 会话的 launchd 环境里(等同 “系统 GUI 会话环境变量”):

  

### **(a) 注入脚本**

{{CODE_BLOCK_4}}

写入:

{{CODE_BLOCK_5}}

并赋权:

{{CODE_BLOCK_6}}

> 为什么这样做?因为 GUI 应用**不会继承你的 shell 文件**,而是继承 **launchd** 的会话环境。用 launchctl setenv 注入到登录会话,就能让 **IDEA、VSCode、Chrome 等**都能读取这些变量。官方与社区都推荐用 **LaunchAgent + launchctl** 实现 GUI 级环境变量,且可跨重启持久生效。    

  

### **(b) LaunchAgent(登录时自动执行脚本)**

{{CODE_BLOCK_7}}

写入:

{{CODE_BLOCK_8}}

加载(或直接重启后自动加载):

{{CODE_BLOCK_9}}

> 说明:早年 ~/.MacOSX/environment.plist 与 /etc/launchd.conf 等方式已被废弃/不再生效,用 LaunchAgent 是现在通行做法。  

---

# **以后使用(只改一个地方)**

  

**新增 / 修改变量** → 只编辑这一个文件:

{{CODE_BLOCK_10}}

保存后让 GUI 会话即刻更新(无需注销):

{{CODE_BLOCK_11}}

终端会话里也会在新开窗口时自动加载;若要当前终端马上生效:

{{CODE_BLOCK_12}}

> 验证:

  

- 终端:printenv DASHSCOPE_API_KEY
    
- IntelliJ:新启动的 IDEA 里运行一个小 Java 程序 System.getenv("DASHSCOPE_API_KEY")
    
- 系统 GUI 会话:launchctl getenv DASHSCOPE_API_KEY
    

---

## **常见问答**

- **为啥不用只改 .zprofile/.zshrc?**
    
    它们只影响 shell;Dock/Spotlight 启动的 GUI 应用看不到。
    
- **为什么不是 ~/.MacOSX/environment.plist?**
    
    已经过时/在新系统上不可靠。
    
- **direnv 要不要用?**
    
    适合按目录自动切换项目变量,但它解决的是**项目级**问题,GUI 全局变量仍需 launchctl 路子;两者可并存。
    

---

需要的话,我可以把以上步骤打包成一键初始化脚本(创建 global.env、脚本、plist 并自动加载)。你只要粘贴运行即可。