# 换项目还得重新 export 一串变量，direnv 接进 Shell 后信任闸门拦在哪？

**URL:** <https://www.shiqidu.com/t/topic/2007>\
**Category:** 开发调优\
**Tags:** 问与答, Linux, 编程, 开源软件, 信息安全\
**Created:** [2026 年11 月 10 日 04:23 UTC](https://www.shiqidu.com/t/topic/2007 "2026-11-10T04:23:11Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

作者： ![shiqidu\_editor](https://www.shiqidu.com/user_avatar/www.shiqidu.com/shiqidu_editor/32/528_2.png) [@shiqidu\_editor](https://www.shiqidu.com/u/shiqidu_editor)\
发布日期： [2026 年11 月 10 日 04:23 UTC](https://www.shiqidu.com/t/topic/2007/1 "2026-11-10T04:23:11Z")

</div>

每个项目一套 `DATABASE_URL`、`API_BASE`、`NODE_OPTIONS`，靠记忆 `source .env` 或往 `~/.bashrc` 里堆 export，迟早会串台：A 项目的密钥还留在 shell 里，就进了 B 项目的终端。[direnv](https://direnv.net/) 的定位很窄：按当前目录自动加载 / 卸载环境变量，把 `.profile` 从项目细节里解救出来。

## 工作方式

每次即将出提示符前，direnv 会检查当前及父目录有没有 `.envrc`（也可配合 `.env`）。文件存在且已授权时，它在 bash 子 shell 里执行，再把导出的变量差分捕回当前 shell。离开目录就卸载。官网强调：它不是把 `.envrc` 直接 eval 进你正在用的那一层 shell，所以别指望靠它导出 alias / function。

支持 bash、zsh、fish、tcsh 等常见 shell。装好二进制之后，必须把 hook 写进对应 rc，否则什么都不会发生。语言无关是它的优点之一：Node、Go、Python 项目可以共用同一套「进目录就备好环境」的习惯，而不必各装一套语言版本管理器只为了塞变量。

## Hook：接到 Shell 上

[Setup / hook](https://direnv.net/docs/hook.html) 按壳分别给了一行。常见两例：

bash（`~/.bashrc` 末尾）：

```bash
eval "$(direnv hook bash)"

```

zsh（`~/.zshrc` 末尾）：

```bash
eval "$(direnv hook zsh)"

```

fish 则是 `direnv hook fish | source`。改完要重启 shell 或重开终端。官网提醒：这行尽量放在其他会改动 prompt 的插件后面，避免抢序。Oh My Zsh 用户也可以按 hook 页把 direnv 加进 plugins 数组，效果同类。

装完先用一个空目录验证 hook：还没 `.envrc` 时不应报错。有未 allow 的文件时应明确拒绝，而不是静默成功。

## `.envrc` + `direnv allow`：信任闸门

官网 Quick demo 的顺序可以原样走一遍：

1. 在项目目录写 `.envrc`（内容是 bash，例如 `export FOO=foo`）。
2. 这时会看到 not allowed。安全机制默认拒绝执行。
3. 你确认文件可信之后，执行 `direnv allow`（或 `direnv allow .`）。
4. 变量出现在当前环境。`cd` 出去后被卸掉。

`allow` 就是信任闸门：任何人（或任意一次 git pull）改了 `.envrc`，都需要重新 allow，避免「克隆下来自动执行陌生脚本」。这和「公司密钥托管、SSO 发令牌」是不同层的事。direnv 管的是本机目录级加载，不要把它理解成绕过公司密钥系统的办法，本文也不写任何绕过步骤。

同事改了共享的 `.envrc` 之后你这边突然不加载，优先怀疑是文件哈希变了、需要重新 allow，而不是 hook 坏了。

## 密钥别进仓库

`.envrc` 若只写非秘密的路径、`PATH_add`、`export NODE_ENV=development`，可以提交，方便同事统一。真正的密钥、会话令牌、客户数据连接串：

不要写进会提交的 `.envrc`。官网建议：stdlib 若在 `.direnv/` 落文件，把 `.direnv/` 加进 `.gitignore`。本地覆盖可用 `.envrc` 里的 `source_env_if_exists .envrc.local`，并忽略 `.envrc.local`。

手滑把密钥推进远端，direnv 帮不了你撤回。那是密钥轮换和仓库清理的问题。allow 只降低「自动执行未审文件」的风险，不代替密钥管理。若团队已有统一的密钥注入方式（CI secret、本机钥匙串、公司代理），让 `.envrc` 只负责「指到那些工具」或加载非敏感默认值，边界会更干净。

## stdlib 一点就够入门

不想手写一长串 `export PATH=$PWD/bin:$PATH`，可用官网提到的 `PATH_add bin` 这类 stdlib 函数，少踩 `$PATH=bin` 这种低级错误。完整函数表见 `direnv-stdlib` man page。需要同时读 `.env` 时，可在 `.envrc` 末尾 `dotenv`，官网写明 `.env` 本身吃不到 stdlib。

个人习惯扩展可以放进 `~/.config/direnv/direnvrc` 或 `lib/*.sh`，在每个 `.envrc` 之前加载。先别急着堆全局魔法，等两三个项目重复同一段再抽。

装包优先用发行版 / 官网安装文档。接好 hook → 写最小 `.envrc` → `direnv allow` → `cd` 进出看加载卸载，比一上来抄复杂集成更稳。提示符里若集成了状态插件，偶发能看到「已加载 / 已卸载」提示，那只是体验增强，真正的依据仍是 `echo` 变量和 `cd` 进出符不符合预期。你们现在是「没 hook」、还是「allow 过但 pull 之后又不加载了」？
