关于 direnv
面向开发者的 direnv 指南,涵盖安装、配置与排错,用于管理项目级 shell 环境。内容包括 `.envrc` 模式、语言布局、安全机制和常见问题。
主要功能
- 01安装与 shell 钩子配置
- 02`.envrc` 配置与允许列表安全机制
- 03环境变量和标准库函数
- 04语言布局与 Nix 集成
- 05排错与配置实践
适用场景
- 01为 shell 配置 direnv
- 02设置项目级变量和路径
- 03加载语言或 Nix 环境
- 04排查环境加载问题
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ julianobarbosa/ claude-code-skills --skill direnv安装后在 Claude Code 中输入/direnv确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/installation.mdstdlib-functions.mdtroubleshooting.md详细介绍
direnv 技能
本技能提供有关使用 direnv 的全面指导,涵盖安装、配置、标准库函数以及按项目管理环境的最佳实践。
何时使用此技能
在以下情况下使用此技能:
- 在 macOS 或 Linux 上安装和配置 direnv
- 为项目创建或修改
.envrc文件 - 设置按项目配置的环境变量
- 配置特定语言的布局(Python、Node.js、Ruby、Go、Perl)
- 将 direnv 与 Nix 或 Nix Flakes 集成
- 为团队管理密钥和环境配置
- 排查环境加载问题
- 创建自定义 direnv 扩展
核心概念
什么是 direnv?
direnv 是一种 shell 扩展,会根据当前目录加载和卸载环境变量。当你使用 cd 进入包含 .envrc 文件的目录时,direnv 会自动加载环境。离开该目录时,它会卸载所做的更改。
安全模型
direnv 使用基于允许列表的安全机制:
- 新建或修改的
.envrc文件必须通过direnv allow明确允许 - 防止自动执行不可信脚本
- 使用
direnv deny撤销访问权限
工作原理
- Shell 钩子拦截目录切换
- 检查当前目录或父目录中是否存在
.envrc文件 - 如果已允许,则在 bash 子 shell 中执行
.envrc - 捕获导出的变量并将其应用到当前 shell
安装
macOS(Homebrew - 推荐)
brew install direnv
Linux
# Ubuntu/Debian
sudo apt install direnv
# Fedora
sudo dnf install direnv
# Arch
sudo pacman -S direnv
# Binary installer (any system)
curl -sfL https://direnv.net/install.sh | bash
验证安装
direnv version
Shell 配置
将钩子添加到 shell 的配置文件中。这是 direnv 正常运行的必要条件。
Zsh (~/.zshrc)
eval "$(direnv hook zsh)"
使用 Oh My Zsh 时:
plugins=(... direnv)
Bash (~/.bashrc)
eval "$(direnv hook bash)"
**重要:**放在 rvm、git-prompt 和其他会修改提示符的扩展之后。
Fish (~/.config/fish/config.fish)
direnv hook fish | source
配置完成后
重启 shell:
exec $SHELL
.envrc 文件基础知识
创建 .envrc
# In your project directory
touch .envrc
# Edit with your preferred editor
vim .envrc
基本语法
# Export environment variables
export NODE_ENV=development
export API_URL=http://localhost:3000
export DATABASE_URL=postgres://localhost/myapp
# The export keyword is required for direnv to capture variables
允许 .envrc
# Allow current directory
direnv allow
# Allow specific path
direnv allow /path/to/project
# Deny/revoke access
direnv deny
标准库函数
direnv 包含功能强大的标准库。请始终优先使用标准库函数,而不是手动导出变量。
PATH 管理
# Prepend to PATH (safer than manual export)
PATH_add bin
PATH_add node_modules/.bin
PATH_add scripts
# Add to arbitrary path-like variable
path_add PYTHONPATH lib
path_add LD_LIBRARY_PATH /opt/lib
# Remove from PATH
PATH_rm "*/.git/bin"
环境文件加载
# Load .env file (current directory)
dotenv
# Load specific file
dotenv .env.local
# Load only if exists (no error)
dotenv_if_exists .env.local
dotenv_if_exists .env.${USER}
# Source another .envrc
source_env ../.envrc
source_env /path/to/.envrc
# Search upward and source parent .envrc
source_up
# Source if exists
source_env_if_exists .envrc.local
语言布局
Node.js:
# Adds node_modules/.bin to PATH
layout node
Python:
# Creates virtualenv in .direnv/python-X.X/
layout python
# Use specific Python version
layout python python3.11
# Shortcut for Python 3
layout python3
# Use Pipenv (reads from Pipfile)
layout pipenv
Ruby:
# Sets GEM_HOME to project directory
layout ruby
Go:
# Modifies GOPATH and adds bin to PATH
layout go
Perl:
# Configures local::lib environment
layout perl
Nix 集成
# Load nix-shell environment
use nix
# With specific file
use nix shell.nix
# Load from Nix flake
use flake
# Load specific flake
use flake "nixpkgs#hello"
use flake ".#devShell"
如需更好地支持 Nix Flakes,请安装 nix-direnv:
# Provides faster, cached use_flake implementation
# https://github.com/nix-community/nix-direnv
版本管理器
# rbenv
use rbenv
# Node.js (with fuzzy version matching)
use node 18
use node 18.17.0
# Reads from .nvmrc if version not specified
use node
# Julia
use julia 1.9
验证
# Require environment variables (errors if missing)
env_vars_required API_KEY DATABASE_URL SECRET_KEY
# Enforce minimum direnv version
direnv_version 2.32.0
# Check git branch
if on_git_branch main; then
export DEPLOY_ENV=production
fi
if on_git_branch develop; then
export DEPLOY_ENV=staging
fi
文件监视
# Reload when files change
watch_file package.json
watch_file requirements.txt
watch_file .tool-versions
watch_file config/*.yaml
# Watch entire directory
watch_dir config
watch_dir migrations
实用函数
# Check if command exists
if has docker; then
export DOCKER_HOST=unix:///var/run/docker.sock
fi
# Expand relative path to absolute
expand_path ./bin
# Find file searching upward
find_up package.json
# Enable strict mode (exit on errors)
strict_env
# Load prefix (configures CPATH, LD_LIBRARY_PATH, etc.)
load_prefix /usr/local/custom
# Load remote script with integrity verification
source_url https://example.com/script.sh "sha256-HASH..."
最佳实践
推荐的 .envrc 模板
#!/usr/bin/env bash
# .envrc - Project environment configuration
# Enforce direnv version for team consistency
direnv_version 2.32.0
# Load .env if exists
dotenv_if_exists
# Load local overrides (not committed to git)
source_env_if_exists .envrc.local
# Language-specific layout
layout node # or: layout python3
# Add project bin directories
PATH_add bin
PATH_add scripts
# Development defaults
export NODE_ENV="${NODE_ENV:-development}"
export LOG_LEVEL="${LOG_LEVEL:-debug}"
# Watch for dependency changes
watch_file package.json
watch_file .nvmrc
Git 配置
.gitignore:
# Environment files with secrets
.env
.env.local
.envrc.local
# direnv virtualenv/cache
.direnv/
提交到仓库:
.envrc(基础配置,不包含密钥).env.example(供团队成员使用的模板)
密钥管理
**切勿提交密钥。**使用环境变量回退值:
# .envrc (committed)
export DATABASE_URL="${DATABASE_URL:-postgres://localhost/dev}"
export API_KEY="${API_KEY:-}"
# Validate required secrets
env_vars_required API_KEY
# .envrc.local (gitignored)
export DATABASE_URL="postgres://user:secret@prod/app"
export API_KEY="actual-secret-key"
分层配置
# ~/projects/.envrc (global dev settings)
export EDITOR=vim
# ~/projects/api/.envrc
source_up
export API_PORT=3000
# ~/projects/api/feature/.envrc
source_up
export FEATURE_FLAG=true
项目结构
my-project/
├── .envrc # Base environment (committed)
├── .envrc.local # Local overrides (gitignored)
├── .env # Environment variables (gitignored)
├── .env.example # Template for team (committed)
└── .direnv/ # direnv cache (gitignored)
自定义扩展
创建 ~/.config/direnv/direnvrc 来定义自定义函数:
#!/usr/bin/env bash
# ~/.config/direnv/direnvrc
# Custom function: Use specific Kubernetes context
use_kubernetes() {
local context="${1:-default}"
export KUBECONFIG="${HOME}/.kube/config"
kubectl config use-context "$context" >/dev/null 2>&1
log_status "kubernetes context: $context"
}
# Custom function: Load from AWS Secrets Manager
use_aws_secrets() {
local secret_name="$1"
local region="${2:-us-east-1}"
eval "$(aws secretsmanager get-secret-value \
--secret-id "$secret_name" \
--region "$region" \
--query SecretString \
--output text | jq -r 'to_entries | .[] | "export \(.key)=\"\(.value)\""')"
log_status "loaded secrets from: $secret_name"
}
# Custom function: Use asdf versions from .tool-versions
use_asdf() {
watch_file .tool-versions
source_env "$(asdf direnv local)"
}
在 .envrc 中使用:
use kubernetes dev-cluster
use aws_secrets myapp/dev
use asdf
命令参考
| 命令 | 说明 |
|---|---|
direnv allow | 允许当前 .envrc |
direnv deny | 撤销 .envrc 的访问权限 |
direnv reload | 强制重新加载环境 |
direnv status | 显示当前状态 |
direnv dump | 转储当前环境 |
direnv edit | 在编辑器中打开 .envrc |
direnv version | 显示 direnv 版本 |
故障排除
环境未加载
# Check status
direnv status
# Force reload
direnv reload
# Re-allow .envrc
direnv allow
# Check if hook is installed
echo $DIRENV_DIR
Shell 钩子问题
- 确认钩子位于 shell 配置文件中
- 确保钩子位于文件末尾
- 完全重启 shell:
exec $SHELL - 检查错误:
direnv hook zsh
性能问题
# Show what's being evaluated
direnv show_dump
# For Nix, use nix-direnv for caching
# https://github.com/nix-community/nix-direnv
调试
# Verbose output
export DIRENV_LOG_FORMAT='%s'
# Show exported variables
direnv dump | jq
# Test .envrc syntax
bash -n .envrc
IDE 集成
VS Code
安装 direnv 扩展,以便在集成终端中自动加载环境。
JetBrains
Neovim
使用 direnv.vim,或使用 lua 进行配置。
常见模式
开发与生产环境
# .envrc
export NODE_ENV="${NODE_ENV:-development}"
if [[ "$NODE_ENV" == "development" ]]; then
export DEBUG=true
export LOG_LEVEL=debug
else
export DEBUG=false
export LOG_LEVEL=info
fi
多服务项目(Monorepo)
# root/.envrc
export PROJECT_ROOT="$(pwd)"
export COMPOSE_PROJECT_NAME=myapp
# services/api/.envrc
source_up
export SERVICE_NAME=api
export SERVICE_PORT=3000
# services/web/.envrc
source_up
export SERVICE_NAME=web
export SERVICE_PORT=8080
Docker 集成
# .envrc
export COMPOSE_FILE=docker-compose.yml
export COMPOSE_PROJECT_NAME="${PWD##*/}"
if has docker-compose; then
export DOCKER_HOST="${DOCKER_HOST:-unix:///var/run/docker.sock}"
fi
# Add Docker bin for containers that install CLI tools
PATH_add .docker/bin
参考资料
注意事项
direnv会在执行cd时重新加载,但编辑.envrc后不会,除非重新进入该目录: 更改.envrc后看起来已生效(没有报错),但要执行cd .或direnv reload才会生效。如果直接在文件中编辑,请对.envrc本身使用watch_file。direnv allow根据文件内容哈希而非路径进行授权: 将.envrc.dev重命名为.envrc再改回来,授权状态仍会保留。但哪怕只改动一个字节也会撤销授权,包括在新编辑器中保存时意外添加的尾随换行符。layout python会创建与主机 Python 版本绑定的.direnv/python-X.Y/: Python 升级会导致虚拟环境悄然失效。可通过layout python python3.11固定版本,或使用rm -rf .direnv && direnv reload重新构建。source_up会搜索所有祖先目录,而不仅是直接父目录:~/或~/Projects/中意外存在的.envrc会影响其中的每个子目录项目。可在目录树深处执行direnv status,查看所有串联的文件并进行检查。- 在
.zshrc中,钩子的放置顺序很重要: 将eval "$(direnv hook zsh)"放在提示符/p10k 设置之后。若放置得更早,precmd钩子会在提示符就绪前触发,direnv 的输出会被提示符重绘覆盖。 PATH_add bin添加的项目相对路径可能会在脚本执行cd到其他位置时出问题: PATH 条目是绝对路径(加载时解析),因此执行cd /tmp && project-binary仍然有效,但运行时读取$PWD/bin的二进制文件则不行。
评论
支持 Markdown