你在 Windows 环境变量里翻一圈,常会看到两个像双胞胎的名字:TMP 和 TEMP。都指向临时目录。问题也就来了:到底该信谁?
答案不漂亮:没有绝对赢家。它不是一次清晰的标准设计,而是一段兼容历史留下的双轨。哪个目录生效,很多时候取决于具体程序先查谁。
两个变量怎么一起活到今天
这事要从 CP/M 说起。CP/M 没有环境变量,也就没有 TMP 或 TEMP。早期程序如果要指定临时文件位置,常见做法是写在程序内部配置里,甚至直接 patch 可执行文件。
MS-DOS 出现后,继承了大量 CP/M 生态,也加入了环境变量。但早期 DOS 程序很多是移植来的。原来不读环境变量,移植后也不会自动关心 TMP 或 TEMP。
后来原生 DOS 程序变多,环境变量开始被当成配置入口。市场没有裁判。TEMP 和 TMP 就都跑出来了。
| 阶段 / 组件 | 对临时目录的选择 | 影响 |
|---|---|---|
| CP/M | 没有环境变量 | 程序靠内部配置或补丁指定临时位置 |
| 早期 MS-DOS 程序 | 多数不看 TMP / TEMP | CP/M 迁移惯性还在 |
MS-DOS 2.0 的 COMMAND.COM | 使用 TEMP | 管道功能需要临时文件落盘 |
Windows 的 GetTempFileName 等相关路径逻辑 | 倾向先看 TMP,再看 TEMP | 使用这类 API 的程序通常更偏向 TMP |
| 普通应用和脚本 | 顺序各不相同 | 最终看程序作者怎么写 |
这里最容易误读。COMMAND.COM 选了 TEMP,不等于所有程序都必须选它。Windows API 偏向 TMP,也不等于所有 Windows 程序都照做。
和 Unix-like 系统里常见的 TMPDIR 相比,Windows 这套更像历史叠层。不是一个变量解决一切,而是旧程序、新 API、脚本习惯一起挤在同一条管道里。
真正受影响的是谁
Windows 开发者最该改的不是记忆口号,而是少自己发明规则。能用系统提供的临时路径和临时文件 API,就别手写一套 TMP / TEMP 查找顺序。否则你写下的顺序,几年后就可能变成别人排查的坑。
脚本作者和运维人员要更现实一点。遇到临时目录异常,别只改一个变量就收工。至少看四件事:
- 这个程序先查
TMP还是TEMP; - 它有没有调用系统 API,还是自己读环境变量;
- 当前进程拿到的是用户变量、系统变量,还是启动时继承下来的旧值;
- 目标目录有没有权限、空间和安全策略限制。
这也是用户变量和系统变量容易制造误会的地方。你在系统设置里改了变量,不代表已经运行的进程立刻感知。服务、终端、IDE、构建工具,各自拿到的环境可能不是同一份。
所以排查顺序很简单:先看程序文档或源码能不能确认查找顺序;再打印进程实际环境;再检查目录权限。别把“我明明改了环境变量”当成证据。那只是你改过,不代表程序用到了。
对研究操作系统历史的人,这个小问题也有价值。它说明兼容性不是抽象美德,而是一种工程交易。平台保住了旧生态,也把不一致留给了后来者。
兼容性不是免费的
我不太买账的是“到底谁才官方”的问法。它把历史兼容问题,误读成了规范选择题。
这不是某个设计师随手多造了一个变量。更准确地说,是平台在迁移生态时不敢切断旧世界。CP/M 程序要能过来,DOS 程序要能跑,Windows 程序还要照顾旧习惯。每一步都有现实理由。
问题也正在这里。
兼容性的账单,通常不由当年的决策者支付。它会转给后来的开发者、运维和用户。你写脚本要兼容两个变量;你排查问题要确认程序顺序;你以为改了系统配置,结果某个老工具根本不认。
这有点像早期铁路轨距。各家公司先把路铺起来,各有理由;等网络连成一片,换轨就成了大工程。两者不完全一样,但底层逻辑相近:局部最优,一旦沉淀成生态,就会变成全局摩擦。
“积习既久,遂成自然。”放在软件史里很准。开发者当年选择 TMP 或 TEMP,多半不是哲学判断,而是顺手、兼容、参考了某个工具。小选择没人管,几十年后就像平台特性。
这件事接下来最该观察的,不是微软会不会突然废掉其中一个变量。这种事成本太高,也没必要。更现实的观察点是:具体工具、构建系统、安装器、老脚本到底遵循哪套顺序。
我的建议很硬:写 Windows 程序,优先交给系统 API;写跨环境脚本,明确自己的查找顺序;做运维排查,直接看进程实际环境。别把兼容遗产当成统一标准。
