direnv
direnv 是一个 shell 环境管理工具,能够在进入目录时自动加载项目特定的环境变量,离开目录时自动卸载。支持 bash、zsh、fish、tcsh 等多种 shell。
# 1. 什么是 direnv
每个项目可能需要不同的环境变量(Node 版本、Python 虚拟环境、API Key、PATH 扩展等)。direnv 通过在项目根目录放置 .envrc 文件,实现:
- 进入目录:自动加载
.envrc中定义的环境变量 - 离开目录:自动恢复之前的环境,不污染全局 shell
- 安全机制:
.envrc修改后需手动direnv allow批准,防止恶意代码自动执行
# 2. 安装
# macOS
brew install direnv
# Ubuntu/Debian
sudo apt-get install direnv
# Arch Linux
sudo pacman -S direnv
2
3
4
5
6
7
8
# 3. 快速开始
# 3.1 配置 shell hook
在 shell 配置文件中添加 hook(根据使用的 shell 选择):
zsh(~/.zshrc)
eval "$(direnv hook zsh)"
bash(~/.bashrc 或 ~/.bash_profile)
eval "$(direnv hook bash)"
fish(~/.config/fish/config.fish)
direnv hook fish | source
配置后重启终端或 source ~/.zshrc 生效。
# 3.2 创建 .envrc
在项目根目录创建 .envrc 文件:
# 项目根目录
cd my-project
# 创建 .envrc
echo 'export NODE_ENV=development' > .envrc
2
3
4
5
此时会看到提示:
direnv: error .envrc is blocked. Run `direnv allow` to approve its content.
# 3.3 批准 .envrc
direnv allow
批准后,进入目录时自动加载环境变量:
direnv: export +NODE_ENV
离开目录时自动卸载:
direnv: export -NODE_ENV
# 4. 常用命令
| 命令 | 说明 |
|---|---|
direnv allow [路径] | 批准当前目录(或指定目录)的 .envrc |
direnv deny [路径] | 拒绝/撤销 .envrc 的批准 |
direnv reload | 重新加载当前目录的 .envrc |
direnv status | 查看 direnv 状态(哪些目录已批准、当前加载情况) |
direnv edit [路径] | 编辑 .envrc(保存后自动 allow) |
direnv exec <目录> <命令> | 在指定目录的环境下执行命令(不切换目录) |
direnv export <shell> | 导出当前目录的环境变量(用于调试) |
direnv version | 查看版本 |
# 5. 常用场景
# 5.1 Node.js 版本管理
配合 nvm 使用:
# .envrc
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && source "$NVM_DIR/nvm.sh"
nvm use v18.20.7
2
3
4
或使用标准库(更简洁):
# .envrc
use node 18.20.7
2
# 5.2 Python 虚拟环境
# .envrc
# 自动激活 venv(如果存在)
layout python-venv
# 或指定虚拟环境目录
layout python-venv .venv
2
3
4
5
6
layout python-venv 会自动:
- 创建虚拟环境(如果不存在)
- 激活虚拟环境
- 离开目录时自动 deactivate
# 5.3 项目环境变量
# .envrc
export API_KEY="your-api-key-here"
export DATABASE_URL="postgres://localhost:5432/mydb"
export DEBUG=true
2
3
4
# 5.4 扩展 PATH
将项目本地的 bin/ 目录加入 PATH:
# .envrc
PATH_add bin
2
或使用标准库:
# .envrc
path_add bin
2
# 5.5 加载 .env 文件
如果项目使用 .env 文件管理环境变量:
# .envrc
dotenv
2
dotenv 会自动加载当前目录的 .env 文件。也可以指定文件名:
dotenv .env.development
# 5.6 多环境切换
# .envrc
# 根据当前 git 分支加载不同环境
if [ "$(git branch --show-current)" = "main" ]; then
export NODE_ENV=production
else
export NODE_ENV=development
fi
2
3
4
5
6
7
# 6. 标准库(stdlib)
direnv 内置了一组实用函数,可在 .envrc 中直接使用:
# 6.1 路径管理
| 函数 | 说明 |
|---|---|
path_add <路径> | 将路径加入 PATH(去重) |
PATH_add <路径> | 同上(兼容旧写法) |
path_rm <路径> | 从 PATH 中移除 |
manpath_add <路径> | 将路径加入 MANPATH |
# 6.2 环境变量
| 函数 | 说明 |
|---|---|
export <变量>=<值> | 设置环境变量(标准 shell 语法) |
unset <变量> | 取消环境变量 |
set -a / set +a | 自动 export 所有变量 |
# 6.3 语言版本管理
| 函数 | 说明 |
|---|---|
use node <版本> | 使用 nvm 切换 Node 版本 |
use ruby <版本> | 使用 rbenv/chruby 切换 Ruby 版本 |
use python <版本> | 使用 pyenv 切换 Python 版本 |
use go <版本> | 切换 Go 版本 |
use java <版本> | 切换 Java 版本 |
use rust <版本> | 切换 Rust 版本 |
# 6.4 虚拟环境
| 函数 | 说明 |
|---|---|
layout python | 创建并激活 Python 虚拟环境(virtualenv) |
layout python-venv | 创建并激活 Python venv(Python 3.3+ 内置) |
layout node | 初始化 Node 项目环境 |
layout ruby | 初始化 Ruby 项目环境 |
layout go | 初始化 Go 项目环境(设置 GOPATH) |
# 6.5 其他实用函数
| 函数 | 说明 |
|---|---|
dotenv [文件] | 加载 .env 文件 |
source_env <文件> | 加载另一个 envrc 文件 |
source_env_if_exists <文件> | 如果文件存在则加载 |
watch_file <文件> | 监视文件变化,变化时自动 reload |
has <命令> | 检查命令是否存在(返回 0/1) |
join_args <args> | 将参数拼接为字符串 |
# 7. 安全机制
direnv 的安全设计是其核心特性之一:
# 7.1 为什么需要 allow
.envrc 是一个可执行的 shell 脚本,如果自动加载可能导致恶意代码执行(例如克隆一个包含恶意 .envrc 的项目后,进入目录就自动执行)。
因此 direnv 要求:
- 首次进入目录:必须手动
direnv allow .envrc修改后:之前的 allow 失效,需要重新direnv allow- 可以随时
direnv deny撤销批准
# 7.2 查看已批准的目录
direnv status
输出示例:
direnv exec path /usr/local/bin/direnv
DIRENV_CONFIG /Users/xxx/.config/direnv
bash_path: /bin/bash
...
Loaded RC path: /path/to/project/.envrc
Allowed RC path: /path/to/project/.envrc
2
3
4
5
6
7
# 7.3 全局白名单
如果信任某个目录下的所有项目,可以在全局配置中设置白名单:
# ~/.config/direnv/direnv.toml
[whitelist]
prefix = ["/path/to/trusted/projects"]
2
3
# 8. 与其他工具集成
# 8.1 与 nvm 集成
# .envrc
use node 18.20.7
2
use node 会自动查找 nvm 并切换版本。如果 nvm 未安装,会提示错误。
# 8.2 与 pyenv 集成
# .envrc
use python 3.11.4
layout python-venv
2
3
# 8.3 与 asdf 集成
# .envrc
use asdf
2
use asdf 会自动读取项目根目录的 .tool-versions 文件。
# 8.4 与 .env 文件集成
很多项目(尤其是 Node.js)使用 .env 文件管理环境变量:
# .envrc
dotenv
2
.env 文件示例:
DATABASE_URL=postgres://localhost:5432/mydb
API_KEY=secret-key
DEBUG=true
2
3
# 8.5 与 VS Code 集成
在 VS Code 中,direnv 加载的环境变量不会自动传递给 VS Code 的终端和调试器。可以安装 direnv 扩展,或在 .vscode/settings.json 中配置:
{
"terminal.integrated.env.osx": {
"DIRENV_LOG_FORMAT": ""
}
}
2
3
4
5
# 9. 常见问题
# 9.1 修改 .envrc 后不生效
修改 .envrc 后,direnv 会自动检测到变化并提示重新 allow:
direnv: error .envrc has changed. Run `direnv allow` to approve its content.
运行 direnv allow 或 direnv reload 即可。
也可以使用 direnv edit 编辑,保存后自动 allow:
direnv edit
# 9.2 进入目录没有反应
检查:
- shell hook 是否配置:
eval "$(direnv hook zsh)" .envrc是否存在于当前目录- 是否已经
direnv allow - direnv 版本是否过旧:
direnv version
# 9.3 环境变量没有卸载
direnv 通过记录进入目录前的环境状态,在离开时恢复。如果环境变量没有正确卸载,可能是因为 .envrc 中使用了不规范的方式修改变量。
正确做法:使用 direnv 标准库函数
# 正确
path_add bin
export MY_VAR=value
# 不推荐(可能导致卸载异常)
export PATH="$PWD/bin:$PATH"
2
3
4
5
6
# 9.4 如何调试 .envrc
# 查看 direnv 会执行什么
direnv export bash
# 在指定目录环境下执行命令
direnv exec /path/to/project env
# 详细日志
DIRENV_DEBUG=1 direnv reload
2
3
4
5
6
7
8
# 9.5 .envrc 应该提交到 git 吗
取决于内容:
- 如果
.envrc只包含公开的环境配置(如 Node 版本、PATH 扩展),可以提交 - 如果包含敏感信息(API Key、密码),不要提交,应加入
.gitignore,并提供.envrc.example模板
推荐做法:
# .gitignore
.envrc
.env
.env.local
# 提交模板
.envrc.example
2
3
4
5
6
7
# 10. 实用技巧
# 10.1 全局配置
创建全局 direnv 配置,所有项目共享:
# ~/.config/direnv/direnvrc
# 这里定义的函数在所有 .envrc 中可用
my_custom_function() {
echo "Hello from global direnv config"
}
2
3
4
5
6
# 10.2 监视文件变化
如果 .envrc 依赖其他文件(如 .nvmrc、.tool-versions),可以监视这些文件:
# .envrc
watch_file .nvmrc
use node $(cat .nvmrc)
2
3
当 .nvmrc 变化时,direnv 会自动 reload。
# 10.3 条件加载
# .envrc
# 只在 macOS 上设置某些变量
if [ "$(uname)" = "Darwin" ]; then
export MACOS_SPECIFIC_VAR=value
fi
# 只在命令存在时加载
if has docker; then
export DOCKER_BUILDKIT=1
fi
2
3
4
5
6
7
8
9
10
# 10.4 项目根目录向上查找
direnv 会从当前目录向上查找 .envrc,直到找到为止。这意味着在项目的子目录中也能正确加载环境变量。
如果子目录有自己的 .envrc,会覆盖父目录的配置。