惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

SecWiki News
SecWiki News
H
Help Net Security
罗磊的独立博客
Stack Overflow Blog
Stack Overflow Blog
M
MIT News - Artificial intelligence
Jina AI
Jina AI
L
LangChain Blog
K
Kaspersky official blog
I
Intezer
Martin Fowler
Martin Fowler
爱范儿
爱范儿
AWS News Blog
AWS News Blog
The Hacker News
The Hacker News
Recorded Future
Recorded Future
人人都是产品经理
人人都是产品经理
H
Hackread – Cybersecurity News, Data Breaches, AI and More
C
CXSECURITY Database RSS Feed - CXSecurity.com
Spread Privacy
Spread Privacy
Simon Willison's Weblog
Simon Willison's Weblog
U
Unit 42
N
News and Events Feed by Topic
A
Arctic Wolf
G
GRAHAM CLULEY
Microsoft Azure Blog
Microsoft Azure Blog
博客园 - 聂微东
F
Fortinet All Blogs
C
Cisco Blogs
美团技术团队
Vercel News
Vercel News
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
H
Hacker News: Front Page
T
Tailwind CSS Blog
I
InfoQ
宝玉的分享
宝玉的分享
Google DeepMind News
Google DeepMind News
博客园 - 司徒正美
P
Palo Alto Networks Blog
A
About on SuperTechFans
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
云风的 BLOG
云风的 BLOG
TaoSecurity Blog
TaoSecurity Blog
Google Online Security Blog
Google Online Security Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
P
Privacy & Cybersecurity Law Blog
H
Heimdal Security Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Hacker News: Ask HN
Hacker News: Ask HN
O
OpenAI News
博客园 - Franky
Scott Helme
Scott Helme

暗无天日

读:AI Agent 安全日志——从可见性与隐私的两难说起 - 暗无天日 AI写作的语言指纹——如何让文字不那么像机器 - 暗无天日 读:50 条 Claude Code 技巧——一个工程经理的六个月使用心得 读:AI 辅助开发为什么让 E2E 测试更有价值 - 暗无天日 读:在Emacs中使用Claude Code(Spacemacs适配版) - 暗无天日 Claude Code 背后的工程哲学——读 Agent Harness Engineering 读:Agent Harness Engineering——AI 智能体不只是模型,还有套件 - 暗无天日 browser-harness:让 AI 直接接管你的浏览器 - 暗无天日 读:Security-First CI/CD —— DevSecOps 自动化实践指南 TIL: 数字小键盘的小数点陷阱与行内算术求值 - 暗无天日 读:Immutability 不是万能药,它是一种权衡 - 暗无天日 Conducty:给 Claude Code 加上项目记忆和并行执行能力 - 暗无天日 读 — GitHub Trending 里的 Claude Code 技能包 读 — Prompt Caching 省钱指南 TIL: Emacs 中那些跟鼠标配合的冷门快捷键 - 暗无天日 读:Anvil——把 Emacs 变成 AI 的工具服务器 读:Emacs 代码折叠终极指南 - 暗无天日 读:Clojure 搭车客指南 - 暗无天日 git推送失败后恢复仓库损坏的完整记录 - 暗无天日 多智能体系统的两个有效模式——以及对 Claude Code 用户的启示 - 暗无天日 用 Org Babel 写 Literate 博文:扩展执行 + 定制导出 proced:Emacs 内置的进程查看器 - 暗无天日 从 proced 定制中学到的 Elisp 模式 读:让 Emacs proced 在 macOS 上显示 CPU 和内存 异步编程的函数着色税 - 暗无天日 链式调用的代价:JavaScript 和 Clojure 的共同教训 - 暗无天日 hyperfine:命令行基准测试工具 - 暗无天日 管道中的变量去哪了?——子 shell 作用域陷阱 - 暗无天日 开源包装器的信任陷阱:四个危险信号 - 暗无天日 程序员愿意为 AI 写文档,却不愿为同事写 - 暗无天日 mktemp: Shell 脚本中临时文件的安全陷阱与最佳实践 - 暗无天日 WSL9x —— 在 Windows 9x 里跑 Linux 内核 6.19 用 ox.el 做你想做的事 —— org-export 高级编程指南 读:Hot-wiring the Lisp Machine —— 用纯 Elisp 构建零依赖的 Org 静态站点生成器 Elisp 性能优化的六个实战教训 - 暗无天日 fcitx5 下 Emacs 无法切换输入法的排查 - 暗无天日 ERT 测试交互命令的三种方式 - 暗无天日 SEM Assistant: 当 Elisp 守护进程遇上 LLM 用 dmsg 给 Elisp 加上结构化调试日志 用 org-habit 追踪非每日习惯 - 暗无天日 Clojure X-Men:当编程语言特性变成超能力 - 暗无天日 TIL: 用 diff-hl 在 fringe 中显示 git 变更 读:llm-test —— 用 LLM agent 驱动 Emacs 测试 TIL: AI 时代的橡皮鸭调试 - 暗无天日 fcitx 启动后键盘输入卡顿的排查 - 暗无天日 TIL: 早期网页的图片热区导航 - 暗无天日 读 Seeing the Whole System 用 Emacs 自动生成每周链接推荐 - 暗无天日 读:ASCII control characters in my terminal 读 What to learn - 暗无天日 Lisp 的括号之痛——一个愚人节玩笑揭开的老伤疤 - 暗无天日 一本书该"线性读"还是"并行读" - 暗无天日 读 How to Monetize a Blog:一篇伪装成变现指南的讽刺文 Python Mock 第三方依赖的四种策略 - 暗无天日 Emacs Lisp 热重载实用指南 - 暗无天日 Prot 的 Emacs 配置哲学 - 暗无天日 TIL: 从直播对谈中学到的三个 Emacs 技巧 - 暗无天日 TIL: 自动使用项目虚拟环境的 Python - 暗无天日 TIL: 让 Help buffer 自动获得焦点 一条命令让本地开发用上 HTTPS —— slim 工具介绍 用 fsck 检查和修复 Linux 文件系统 排查Linux进程"卡死"实战:从strace到gdb全流程 - 暗无天日 PostgreSQL 索引:从基础到你可能不知道的高级用法 - 暗无天日 用 .pdbrc 自定义 Python 调试器 ANSI 转义码的标准化现状 - 暗无天日 终端程序的潜规则 - 暗无天日 PARA Org-mode 测试配置 - 暗无天日 AI越强越辣鸡?控制论说这是必然的 - 暗无天日 AI 越强越需要你盯着——反馈循环实操指南 - 暗无天日 你的AI代理正在偷你的密钥——四种你没想到的泄露通道 - 暗无天日 LLM 在 DevOps 中的三种角色 - 暗无天日 写作风格的反建议 - 暗无天日 反驳本质复杂性——Dan Luu 论为什么《没有银弹》错了 - 暗无天日 文件充满了危险——Dan Luu 谈文件系统的可靠性陷阱 - 暗无天日 AI 时代的 PARA 方法:用 Org-mode 和 AI 打造个人知识管理系统 Linux 数据去重学习笔记 - 暗无天日 创建跨平台 ZIP 文件的隐藏陷阱:Extra Field - 暗无天日 X11 Forwarding 排障指南 - 暗无天日 IP欺骗端口扫描:当别人冒充你去扫描别人 - 暗无天日 Linux 输入栈全景解析:从硬件按键到屏幕响应 - 暗无天日 Unix 系统中那些被埋没的配置开关——以 FontConfig 为例 - 暗无天日 在Linux上限制儿童使用电脑 - 暗无天日 GIF不仅仅是一种图片格式——用GIF流做些奇怪的事 - 暗无天日 Leiningen 学习笔记:Clojure 项目构建与管理从入门到实战配置 - 暗无天日 Google SRE Book 读书笔记 - 暗无天日 yes 管道 head 发生了什么 - 暗无天日 为什么 nohup 在 crontab 中不起作用 Bash中的Indirection与Nameref - 暗无天日 Linux PAM 简介 - 暗无天日 从Linux ISO文件启动计算机 - 暗无天日 用 Bash 打造一个Screen Locker 用GitHub Actions自动构建EGO博客 - 暗无天日 blocking I/O 的作用 - 暗无天日 mobileog 手机端同步提示Error:2 No such file 的解决方法 回收 WSL2 VHDX 文件占用空间 使用 org-mode columnview 生成任务列表 - 暗无天日 Emacs 作为 MPD 客户端 - 暗无天日 移动文件路径却不破坏org file link的方法 - 暗无天日 如何合理的导出help link 成HTML - 暗无天日 笑话理解之Biology - 暗无天日
Emacs 缓存文件大扫除,用一个 alist 管住所有散落状态 - 暗无天日
lujun9972 · 2026-06-22 · via 暗无天日

目录

  • 设计思路,一个 alist 驱动一切
  • 一,先定义缓存文件存放的根目录
  • 二,alist 作为唯一配置源
  • 三,两个辅助函数
  • 拼接起来
  • 特殊变量处理
    • auto-save
    • TRAMP 等延迟加载变量
  • 完整代码 + 最终效果
  • 新包接入,两行配置
  • 与 no-littering 的对比

打开你的 ~/.emacs.d ,看看里面有什么。

bookmarks、auto-saves、recentf、savehist、transient、tramp、url、multisession、image-dired、erc、rcirc……乱七八糟一堆文件。这些文件散落各处,想清不敢清,想管没头绪。

Rahul M. Juliato 在 Taming Emacs Cache and Temporary Files 一文中,给出了一套不依赖任何外部包的整理方案,把所有这些缓存文件集中管起来。

设计思路,一个 alist 驱动一切

Emacs 内部有一堆变量控制各类缓存文件的存放位置。比如 bookmark-default-file 管书签存哪, recentf-save-file 管最近打开的文件列表存哪, tramp-persistency-file-name 管 TRAMP 的连接信息存哪,等等。每个变量各自指向一个路径,默认散落在不同目录中。

为了解决这个问题,我们可以:

  1. 定义一个 alist,把变量名映射到相对路径
  2. 一个辅助函数把 key 解析成绝对路径
  3. 另一个辅助函数在Emacs启动时自动创建所有需要的目录,相当于 mkdir -p
  4. use-package :custom 把所有变量指向这些路径

这样一来, 我们可以把所有缓存文件放到一个目录中进行管理,想清空就 rm -rf ,想备份就整个目录打包。

一,先定义缓存文件存放的根目录

这里用 defcustom ,不是 defvar 来定义根目录。

defcustom 是 Emacs 中专门定义「用户可配置选项」的宏。它和 defvar 最大的区别,是 defcustom 定义的变量会出现在 M-x customize 界面里,你可以用图形化的方式进行修改,还能把值持久化到 custom.el 中。这样一来,你不需要手工修改配置文件就能切换缓存位置。

(defcustom my/cache-directory
  (expand-file-name "cache/" user-emacs-directory)
  "Base directory for Emacs cache files.
All entries in `my/cache-paths' are resolved relative to this
directory.  Choose one of the presets or supply any custom path.
Changes take effect after restarting Emacs."
  :type `(choice
          (const     :tag "Inside Emacs config  (cache/ in user-emacs-directory)"
                     ,(expand-file-name "cache/" user-emacs-directory))
          (const     :tag "System temp          (/tmp/emacs-cache/)" "/tmp/emacs-cache/")
          (directory :tag "Custom directory"))
  :group 'my)

三个预设选项:

  • config 内 (默认),缓存放 ~/.emacs.d/cache/ ,和配置放一起,持久化留存
  • /tmp ,缓存放 /tmp/emacs-cache/ ,重启即清空,适合录屏或测试
  • 自定义路径 ,随便指到哪

注意默认值用的是 user-emacs-directory 而不是硬编码 ~/.emacs.d/ 。原因是 Emacs 29 引入了 --init-directory 命令行参数,你可以 emacs --init-directory=/path/to/config 这样启动 Emacs 。用 user-emacs-directory 锚定路径,缓存会跟着当前启动的配置走,而不是死板的都往默认 ~/.emacs.d 里写。

最后还需要一个小补丁,需要在 init.el 中指明重新加载 custom-file

(when custom-file
  (load custom-file 'noerror 'nomessage))

二,alist 作为唯一配置源

第二个组件是一个 alist,用变量名作 key,相对路径作 value。整个方案里所有「什么东西该放哪」的信息都集中在这一处。

(defvar my/cache-paths
  '(
        (bookmark-file               . "bookmarks")
    (ielm-history-file-name      . "ielm-history.eld")
    (project-list-file           . "projects")
    (recentf-save-file           . "recentf")
    (savehist-file               . "history")
    (save-place-file             . "saveplace")
    (transient-history-file      . "transient/history.el")
    (transient-levels-file       . "transient/levels.el")
    (transient-values-file       . "transient/values.el")
    (tramp-persistency-file-name . "tramp")
    (nsm-settings-file           . "network-security.data")
        (auto-saves                  . "auto-saves/")
    (auto-saves-sessions         . "auto-saves/sessions/")
    (multisession-directory      . "multisession/")
    (url-configuration-directory . "url/")
    (image-dired-dir             . "image-dired/")
    (erc-log-channels-directory  . "erc/logs/")
    (erc-image-cache-directory   . "erc/images/")
    (rcirc-log-directory         . "rcirc/logs/"))
  "Alist of (KEY . RELATIVE-PATH) for cache locations.
RELATIVE-PATH is resolved against `my/cache-directory'.
A trailing slash on RELATIVE-PATH marks the entry as a directory.")

这里遵照 directory-name-p 函数的约定,value 以 / 结尾的是目录,否则是文件。后面的辅助函数会根据这个来区分要不要自动创建目录。

这里作者故意把 tree-sitter/eln-cache/eshell/ 排除在外。因为 tree-sittereln-cache 的内容来自耗时较长的编译过程(语法树编译、本地代码编译),重新生成成本太大。 eshell 里的是命令历史和别名,更接近 dotfile,算不上临时状态。大家的工作流可能不同,按需增减就行。

三,两个辅助函数

两个辅助函数,一个解析路径,一个创建目录。

(defun my/cache--path (key)
  "Return the absolute path for KEY in `my/cache-paths'."
  (let ((rel (cdr (assq key my/cache-paths))))
    (unless rel
      (error "my/cache--path: Unknown key %S" key))
    (expand-file-name rel my/cache-directory)))

my/cache--pathassq 查找 key。 assq 对 symbol key 用 eq 做比较(比 assocequal 更快)。 unless 分支在你 :custom 块里打错了变量名的情况下给你错误提示。没有这个错误检查的话,Emacs 会静默地把值设成 nil ,然后很多包在 nil 路径上写文件,会出现莫名其妙的错误。

(defun my/cache--ensure-dirs ()
  "Create every directory referenced by `my/cache-paths'.
Entries ending in `/' are created directly; other entries have their
parent directory created."
  (dolist (entry my/cache-paths)
    (let* ((abs (my/cache--path (car entry)))
           (dir (if (directory-name-p abs)
                    abs
                  (file-name-directory abs))))
      (make-directory dir t))))

(my/cache--ensure-dirs)

my/cache--ensure-dirs 在启动时跑一遍,确保所有需要的目录都存在。 make-directory 的第二个参数 t 相当于 mkdir -p ,所以 transient/history.el 会自动创建 <cache>/transient/ 目录,哪怕 alist 里没单独列出它。

拼接起来

把三部分内容拼接起来就行了。

大部分缓存变量可以直接在 use-package emacs:custom 块里设:

(use-package emacs
  :ensure nil
  :custom
  (bookmark-file               (my/cache--path 'bookmark-file))
  (ielm-history-file-name      (my/cache--path 'ielm-history-file-name))
  (project-list-file           (my/cache--path 'project-list-file))
  (recentf-save-file           (my/cache--path 'recentf-save-file))
  (savehist-file               (my/cache--path 'savehist-file))
  (save-place-file             (my/cache--path 'save-place-file))
  (transient-history-file      (my/cache--path 'transient-history-file))
  (transient-levels-file       (my/cache--path 'transient-levels-file))
  (transient-values-file       (my/cache--path 'transient-values-file))
  (nsm-settings-file           (my/cache--path 'nsm-settings-file))
  (multisession-directory      (my/cache--path 'multisession-directory))
  (url-configuration-directory (my/cache--path 'url-configuration-directory))
  )

特殊变量处理

:custom 块适合大部分变量,但有几种情况需要单独处理。

auto-save

auto-save 用的不是单值变量,而是路径变换规则:

(setq auto-save-list-file-prefix (my/cache--path 'auto-saves-sessions)
      auto-save-file-name-transforms
      `((".*" ,(my/cache--path 'auto-saves) t)))

auto-save-list-file-prefix 控制「待恢复文件列表」的存放位置( M-x recover-session 读的就是它)。 auto-save-file-name-transforms 是一个 (REGEX REPLACEMENT UNIQUIFY) 三元组列表,第三个元素 t (uniquify 标志)会把原始路径编码进文件名,这样两个同名的文件就不会在 auto-save 目录里互相覆盖了。

TRAMP 等延迟加载变量

use-package emacs:custom 块在启动时就执行了,但有些变量的定义来自后续才加载的包。包还没加载就去设它的变量,轻则无效,重则报错。遇到这种,把 setopt 放进对应包的 :config 块:

(use-package tramp
  :config
  (setopt tramp-persistency-file-name (my/cache--path 'tramp-persistency-file-name)))

setoptsetq 的现代替代品,专门用于 defcustom 定义的变量。关键区别在于: setopt 会执行变量定义的 :set 函数(如果有的话), setq 直接赋值则会跳过这一步。 tramp-persistency-file-name 定义了 :set 函数来触发 TRAMP 内部重载,用 setq 会跳过这一步,导致 TRAMP 状态不一致。

完整代码 + 最终效果

所有组件拼在一起,就是这样:

(defcustom my/cache-directory
  (expand-file-name "cache/" user-emacs-directory)
  "Base directory for Emacs cache files."
  :type `(choice
          (const     :tag "Inside Emacs config  (cache/ in user-emacs-directory)"
                     ,(expand-file-name "cache/" user-emacs-directory))
          (const     :tag "System temp          (/tmp/emacs-cache/)" "/tmp/emacs-cache/")
          (directory :tag "Custom directory"))
  :group 'my)

(when custom-file
  (load custom-file 'noerror 'nomessage))

(defvar my/cache-paths
  '((bookmark-file               . "bookmarks")
    (ielm-history-file-name      . "ielm-history.eld")
    (project-list-file           . "projects")
    (recentf-save-file           . "recentf")
    (savehist-file               . "history")
    (save-place-file             . "saveplace")
    (transient-history-file      . "transient/history.el")
    (transient-levels-file       . "transient/levels.el")
    (transient-values-file       . "transient/values.el")
    (tramp-persistency-file-name . "tramp")
    (nsm-settings-file           . "network-security.data")
    (auto-saves                  . "auto-saves/")
    (auto-saves-sessions         . "auto-saves/sessions/")
    (multisession-directory      . "multisession/")
    (url-configuration-directory . "url/")
    (image-dired-dir             . "image-dired/")
    (erc-log-channels-directory  . "erc/logs/")
    (erc-image-cache-directory   . "erc/images/")
    (rcirc-log-directory         . "rcirc/logs/"))
  "Alist of (KEY . RELATIVE-PATH) for cache locations.")

(defun my/cache--path (key)
  "Return the absolute path for KEY in `my/cache-paths'."
  (let ((rel (cdr (assq key my/cache-paths))))
    (unless rel
      (error "my/cache--path: Unknown key %S" key))
    (expand-file-name rel my/cache-directory)))

(defun my/cache--ensure-dirs ()
  "Create every directory referenced by `my/cache-paths'."
  (dolist (entry my/cache-paths)
    (let* ((abs (my/cache--path (car entry)))
           (dir (if (directory-name-p abs) abs
                   (file-name-directory abs))))
      (make-directory dir t))))

(my/cache--ensure-dirs)

(use-package emacs
  :ensure nil
  :custom
  (bookmark-file               (my/cache--path 'bookmark-file))
  (ielm-history-file-name      (my/cache--path 'ielm-history-file-name))
  (project-list-file           (my/cache--path 'project-list-file))
  (recentf-save-file           (my/cache--path 'recentf-save-file))
  (savehist-file               (my/cache--path 'savehist-file))
  (save-place-file             (my/cache--path 'save-place-file))
  (transient-history-file      (my/cache--path 'transient-history-file))
  (transient-levels-file       (my/cache--path 'transient-levels-file))
  (transient-values-file       (my/cache--path 'transient-values-file))
  (nsm-settings-file           (my/cache--path 'nsm-settings-file))
  (multisession-directory      (my/cache--path 'multisession-directory))
  (url-configuration-directory (my/cache--path 'url-configuration-directory))
  (create-lockfiles            nil)
  (make-backup-files           nil)
  (auto-save-default           t)
  :config
  (setq auto-save-list-file-prefix (my/cache--path 'auto-saves-sessions)
        auto-save-file-name-transforms
        `((".*" ,(my/cache--path 'auto-saves) t))))

(use-package tramp
  :config
  (setopt tramp-persistency-file-name (my/cache--path 'tramp-persistency-file-name)))

启动后, ~/.emacs.d/ 的目录结构会变成这样:

.
├── cache
│   ├── auto-saves
│   │   └── sessions
│   ├── erc
│   │   ├── images
│   │   └── logs
│   ├── image-dired
│   ├── multisession
│   ├── rcirc
│   │   └── logs
│   ├── transient
│   │   ├── history.el
│   │   ├── levels.el
│   │   └── values.el
│   ├── url
│   ├── bookmarks
│   ├── history
│   ├── ielm-history.eld
│   ├── network-security.data
│   ├── projects
│   ├── recentf
│   ├── saveplace
│   └── tramp
├── eln-cache
│   └── 32_0_50-25c5b284
├── eshell
│   └── history
├── init.el
└── tree-sitter
    ├── libtree-sitter-markdown-inline.dylib
    └── libtree-sitter-markdown.dylib

所有缓存都在 cache/ 下一个目录,想清就清,想留就留。 eln-cacheeshelltree-sitter 是编译产出和 dotfile,不在 alist 管理范围内。

新包接入,两行配置

每当你开始用一个会产生缓存文件的新包,只需要两处改动:

  1. my/cache-paths 里加一条映射
  2. use-package :custom 里加一行

下次重启时,目录自动创建,变量自动指向新地址。

与 no-littering 的对比

原文提到,如果你不想自己维护这套配置,还可以用 no-littering 这个包。两种方案各有利弊:

方案 优点 缺点
自己维护 alist 完全可控,零外部依赖,知道每个文件在干什么 新增包时需手动加映射
no-littering 开箱即用,覆盖面广,社区维护 多一个依赖,定制不如自己写的灵活

如果你的包列表稳定、不经常增减,自己维护 alist 是更轻量的选择。如果经常尝试新包、不想费心记路径变量名,no-littering 省事。