关于重复的 GPG_TTY 与隐藏的 .zprofile 元凶的奇案(以及我的终端为何如此缓慢!)

发布: (2026年1月14日 GMT+8 23:07)
8 min read
原文: Dev.to

Source: Dev.to

封面图片:重复的 GPG_TTY 与隐藏的 .zprofile 元凶之谜(以及我的终端为何如此缓慢!)

图片来源: joffi 来自 Pixabay

偶尔,一个看似微不足道的配置怪癖会演变成让人抓狂的谜团,尤其是在点文件(dotfiles)的世界里。我最近就陷入了这样的困境:在我的 .zshrc 文件中,下面这行

export GPG_TTY=$(tty)

每次打开新终端时都会神奇地重新出现。最初只是个小恼人,但很快就变成了一次对 shell 启动逻辑、Oh My Zsh 插件以及常被忽视的点文件管理细节的深入探索——最终导致我的终端启动速度受到显著影响。

问题:令人沮丧的缓慢启动(以及 .zshrc 中的巨大秘密)

它始于一种细微、随后愈发痛苦的意识:我的终端启动速度变得极其缓慢。曾经瞬间弹出的窗口现在要爬行几秒钟。我打开 Cursor、Terminal.app 或 Tabby,只能……等。

在数天的挫败感之后,我打开了自己的 .zshrc。令我极度惊恐的是,里面已经堆满了成千上万行相同的代码:

export GPG_TTY=$(tty)

每一行冗余的代码在我每次打开新终端会话时都会被 Zsh 解析并执行,使得原本敏捷的启动变成了令人沮丧的等待。

GPG_TTY 环境变量对于 GnuPG(GPG)来说至关重要,它告诉 GPG 使用哪个终端来提示输入密码——这对签署 Git 提交至关重要。因此,虽然这行代码本身很重要,但它被无情地复制(至少 3000 次!)显然是不应该的。

初步怀疑:Oh My Zsh 插件

我的第一想法,和许多 Zsh 用户一样,直接想到 Oh My Zsh 及其众多插件。我知道有一个 gpg-agent 插件,而且它的 README.md 明确写着:

“在每次 shell 执行前更新 GPG_TTY 环境变量。”

快速 grep 确认该插件包含 export GPG_TTY=$TTY 这一行。这说明插件确实在管理该变量。我的最初理论是插件可能有缺陷,持续向我的 .zshrc 写入。但插件通常是 sourced(被加载),而不是设计成写回用户的主配置文件。这是一个关键的区别。

如果插件在设置变量,而我又看到它 追加.zshrc,这就暗示出现冲突:插件在完成它的工作,但还有别的东西在写入冗余的那一行。

寻找作者的线索

知道这行是被 写入(而不仅仅是被 source)是关键。我系统地尝试了:

操作结果
.zshrc 中删除重复的行它又出现了
禁用 gpg-agent 插件该行仍然重新出现
.zshrc 中注释掉 source $ZSH/oh-my-zsh.sh(完全禁用 Oh My Zsh)该行仍然重新出现

这确认了罪魁祸首在 Oh My Zsh 直接影响之外,意味着它可能位于另一个 Zsh 启动文件或系统级脚本中。

Zsh 按特定顺序处理多个启动文件:

  1. ~/.zshenv
  2. ~/.zprofile(登录 shell)
  3. ~/.zshrc(交互式 shell)
  4. ~/.zlogin(登录 shell,在 .zshrc 之后)

鉴于登录 shell 的行为,.zprofile 成为主要嫌疑对象。

灵光一现:.zprofile 揭开了它的秘密

打开我的 ~/.zprofile 文件,我立刻发现了它:

# https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key
# Add GPG key
if [ -r ~/.zshrc ]; then
    echo -e '\nexport GPG_TTY=$(tty)' >> ~/.zshrc
else
    echo -e '\nexport GPG_TTY=$(tty)' >> ~/.zprofile
fi

就是它——一段脚本,似乎直接取自 GitHub 关于管理提交签名验证的文档,旨在 添加 export GPG_TTY=$(tty) 到我的 .zshrc

在这里我必须承认自己的错误。回顾文档时,显而易见这条命令本应 一次性 作为终端命令运行,以初始化配置。结果,我错误地复制了逻辑并直接粘贴进了 ~/.zprofile

致命缺陷: 该脚本缺乏 幂等性。它没有检查 GPG_TTY 是否已经存在于 ~/.zshrc 中。每次登录 shell 启动并读取 .zprofile 时,它都会检查 .zshrc 是否可读(始终可读),随后 无条件地追加 该行。由于 .zprofile 在每次新登录会话时都会运行,它实际上在我每次打开 IDE 或登录时,都在我的 .zshrc 中“刷屏”添加新的 export。

修复方法及经验教训

  1. 删除重复项 – 我回到 ~/.zshrc,手动删除了所有成千上万的重复 export GPG_TTY=$(tty) 行。
  2. 移除有问题的块 – 我删除了 ~/.zprofile 中整个 if … else … fi 块。

之后,打开新终端时我的 .zshrc 保持干净,GPG_TTY 仍然被正确设置(多亏了 Oh My Zsh gpg-agent 插件),最重要的是,终端启动速度恢复了流畅!

关键经验教训

  • 幂等性是点文件的王道。 任何修改配置文件的脚本都应首先检查是否真的需要进行更改。否则会导致臃肿、混乱以及显著的性能下降。
  • 了解你的 shell 启动文件。 熟悉 .zshenv.zprofile.zshrc.zlogin 等文件的加载顺序和作用,对有效管理点文件至关重要。
  • 不要把整段命令直接复制粘贴到启动文件中。 文档中常常展示一次性命令;如果把这些逻辑嵌入会被重复执行的文件中,需要对其进行改造,使其能够安全重复运行。

有了这些要点,我的终端再次变快,点文件也保持整洁——直到下一个谜团出现!

故障排除

第三方文档可能是一把双刃剑:虽然 GitHub 的指南本意是帮助用户,但为 Zsh 用户提供的特定脚本如果被执行多次,尤其是放在像 .zprofile 这样经常运行的文件中,可能会导致问题。

信任专用工具: 如果你使用的是像 Oh My Zsh 的 gpg-agent 这样的插件来管理特定的环境变量,通常最好让它来完成繁重的工作。

这次小小的冒险让我对自己的 shell 环境有了更深入的了解。虽然经历了几天的挫败感,但最终破解谜团、恢复快速终端启动的满足感是值得的。保持 dotfiles 整洁,并始终警惕那些在未检查的情况下直接追加内容的脚本!

Back to Blog

相关文章

阅读更多 »

Rapg:基于 TUI 的密钥管理器

我们都有这种经历。你加入一个新项目,首先听到的就是:“在 Slack 的置顶消息里查找 .env 文件”。或者你有多个 .env …

技术是赋能者,而非救世主

为什么思考的清晰度比你使用的工具更重要。Technology 常被视为一种魔法开关——只要打开,它就能让一切改善。新的 software,...

踏入 agentic coding

使用 Copilot Agent 的经验 我主要使用 GitHub Copilot 进行 inline edits 和 PR reviews,让我的大脑完成大部分思考。最近我决定 t...