先说个个人经历。2020年底我帮某实验室的同学调试一台刚装好TeX Live 2020的机器,打开终端输入pdflatex还没开始编译,就弹出一行红字:I can't find the format file 'pdflatex.fmt'!。后来在部署自动化编译环境的时候,这种报错又反复出现过好几次。这个报错表面上看很吓人,实际上原因就那么几个:要么格式文件根本没生成,要么生成后被放到了别处,要么你调用的pdflatex压根不是TeX Live 2020自己那个。
这个报错用大白话翻译一下:你启动的pdfLaTeX引擎,在它认为应该放格式文件的地方,找不到pdflatex.fmt这个预编译格式文件。写文章的人最容易在这里被卡住,因为报错信息又短又专业,搜索时会看到一堆让你重装系统的建议,但其实完全不用。下面就把我这些年排查和修复这个问题的完整思路、具体命令和踩过的坑一次说清楚。
1. 先弄明白pdflatex.fmt是什么,为什么会出现这个报错
1.1 fmt文件到底是什么角色
TeX系统里的fmt文件,全称是format file,中文一般叫“格式文件”。它本质上是一个预编译产物:把LaTeX内核(latex.ltx)、基础宏包以及一堆底层配置,提前读一遍并编译成一个文件,引擎启动时直接加载这个文件,而不需要每次都现场逐行解析宏代码。
我在给新人讲这个概念时,经常用一个类比。你把fmt文件想象成一间餐厅已经提前包好的饺子,顾客点餐后,厨师只需要把饺子下锅煮熟就行,不需要临时和面、擀皮、调馅。pdflatex.fmt就是pdfLaTeX这个引擎的“速冻饺子”,里面包含了LaTeX格式的完整状态:宏展开表、寄存器分配、内部参数预设值,甚至字体的加载信息。
这个设计的最大好处就是快。你直接运行pdflatex编译一个文档,启动过程通常在几百毫秒内完成;如果没有fmt文件,每次都要现场加载和编译LaTeX内核,几秒钟可能都打不住。代价就是fmt文件对路径、版本和依赖环境非常敏感,一旦TeX系统里的文件布局变了、版本换了、或者缓存目录被清理,它就会找不到或者失效,结果就是上面那个报错。
1.2 出现这个报错的三类典型场景
根据我这几年实际遇到的情况,这个报错基本逃不出下面三类:
第一类是格式文件压根没有生成。最典型的就是用官方install-tl脚本安装TeX Live 2020时网络中断、中途取消,或者选了某个过于精简的安装方案。安装程序可能把二进制和宏包都放好了,但web2c目录下的fmt文件没有完整生成,或者生成到一半被打断。此时你调用pdflatex,引擎找不到fmt,直接报错。
第二类是文件其实存在,但你调用的pdflatex不是你以为的那个。这种情况在Linux上尤其多。比如系统里原本有发行版自带的TeX Live,路径是/usr/bin/pdflatex,后来你又用官方脚本装了2020版本,装好之后没把PATH配置到位。打开终端执行pdflatex,系统默认找到的还是/usr/bin下的旧版本。旧版引擎不会去看2020的texmf-var目录,自然找不到匹配的格式文件。
第三类是文件确实被删了或者被破坏了。有些朋友为了省磁盘空间,手动清理过texmf-var目录,把里面看成“缓存”一顿删;还有的是Windows上杀毒软件把fmt文件当可疑文件隔离了。这种属于物理层面上的缺失,处理方式也很直接,重新生成即可。
2. 三分钟定位问题源头:先查哪个pdflatex、再查fmt在不在
遇到这个报错,我不建议上来就直接重装。先花三分钟做两个检查,往往比折腾半小时安装有效得多。
2.1 先确认你调用的pdflatex到底是哪个
第一个检查是看当前环境的pdflatex指向哪里。Linux和macOS上执行:
which pdflatexWindows上执行:
where pdflatex输出会给你一个绝对路径。比如我在某台机器上看到/usr/local/texlive/2020/bin/x86_64-linux/pdflatex,这就是官方版;如果看到/usr/bin/pdflatex,那多半是发行版自带的旧版或系统级包装。输出路径确认之后,再执行一下版本号:
pdflatex --version如果版本号显示的不是2020,或者版本号与实际安装的年份对不上,那问题基本就锁定了:PATH环境变量里旧版本的优先级太高,导致你调用的根本不是自己想用的引擎。
这里还有一个很隐蔽的情况:版本号显示的确实是2020,但终端环境是从旧会话继承下来的,PATH里还保留着旧版本的路径。比如通过IDE、SSH、定时任务等启动的进程,可能不会重新读取你新修改的~/.bashrc或~/.zshrc,于是它们启动pdflatex时使用的环境还是旧的。
2.2 再看fmt文件到底在不在
确认调用者没问题之后,第二步是检查格式文件是否存在。最直接的办法是让kpsewhich帮你定位:
kpsewhich pdflatex.fmt如果它输出了一个路径,比如/usr/local/texlive/2020/texmf-var/web2c/pdftex/pdflatex.fmt,说明文件是存在的。此时你还继续报错,那就要考虑二进制与格式文件版本不匹配、或者多个用户共用同一套TeX Live时权限错乱的问题。
如果kpsewhich没有输出任何内容,说明引擎确实找不到fmt文件,也就可以确认没有成功生成或已经被删了。想看得更细一点,可以用kpsewhich查看TeX系统的几个关键目录:
kpsewhich --var-value TEXMFVAR kpsewhich --var-value TEXMFDIST然后直接查看web2c目录下是否有对应文件。比如在Linux上,TEXMFVAR通常指向/usr/local/texlive/2020/texmf-var,那么你可以执行:
ls -la $(kpsewhich -var-value TEXMFVAR)/web2c/pdftex/这里要特别提醒一句:不要看到fmt文件不存在,就手动创建一个空文件或从别的机器拷贝一个同名文件过来冒充。fmt文件是和具体引擎版本、宏包版本强相关的二进制产物,直接拷贝很容易因为版本不匹配而失效,手动创建的空文件更会让问题从“找不到”变成“找到了但格式非法”,排查起来更绕。
3. 实操修复:一个命令快速重建pdflatex格式,及彻底重建方案
定位完问题之后,修复手段其实并不复杂。TeX Live提供了一个专门管理格式文件的工具:fmtutil。在2020版本中,常见的命令有fmtutil和fmtutil-sys两种,前者操作用户级目录,后者操作系统级目录。绝大部分场景下用的是系统级。
3.1 快速方案:只重建pdflatex这一个格式
如果你确认只是pdflatex.fmt缺失、损坏,最省时间的办法是指定单个格式重建,不碰其他引擎。
sudo fmtutil-sys --byfmt pdflatex执行之后,终端会输出类似这样的信息:
pdftex -ini -jobname=pdflatex -progname=pdflatex -8bit -output-directory=...整个过程通常几十秒内就能完成。完成后立刻可以用一个小文档验证:
echo '\documentclass{article}\begin{document}hello\end{document}' > test.tex pdflatex -interaction=nonstopmode test.tex如果不再报原本的错,能看到正常的pdf输出,那问题就解决了。之所以推荐先走单点重建而不是一上来就全量,是因为单点重建速度快、输出少、排查方便。全量重建在部分机器上可能要跑十几分钟,如果你只是想尽快恢复编译,没必要为这个问题付出那么大的时间成本。我在实际维护中,至少有一半以上的情况是靠这一条命令救回来的。
3.2 彻底方案:全量重建所有格式文件
如果--byfmt pdflatex执行后仍然失败,或者你发现不只是pdflatex,连xelatex、lualatex等也会报类似的格式缺失错误,那就需要全量重建:
sudo fmtutil-sys --all这条命令会扫描系统里所有引擎对应的格式文件,把缺失的、过期的、损坏的全部重新生成。执行时输出会非常长,几百行很正常。我在一些配置不算高的机器上跑过,耗时5到15分钟不等,中途千万不要觉得卡死了就强制中断,退出只会让更多格式处于半生成状态。
全量命令跑完后,顺手把用户级缓存也刷一遍,避免个别二进制从用户目录读到旧数据:
fmtutil --all日常使用中,你可能会碰到这样的疑问:系统级和用户级到底要跑哪个?我的经验是,如果是官方安装脚本默认装到/usr/local/texlive下的,优先用sudo运行fmtutil-sys;如果你是装在用户目录下的便携版,或者你平时编译用的是用户树下的配置,就运行不带sys的fmtutil。两者都跑一遍也不会有什么坏处,只是多花一点时间。
3.3 路径问题导致报错的另类修复:调整PATH
还有一种情况,fmt文件一切正常,但which pdflatex指向的就是旧版本,或者指向了另一个TeX发行版。这种时候重建fmt治标不治本,你要做的是让系统找到正确的二进制。
在Linux和macOS上,最直接的方法是把TeX Live 2020的bin目录加到PATH最前面:
export PATH=/usr/local/texlive/2020/bin/x86_64-linux:$PATH64位Linux的bin目录一般是x86_64-linux,macOS上一般是universal-darwin,具体目录名可以通过ls /usr/local/texlive/2020/bin/查看。确认目录名后,把上面这行写进~/.zshrc或~/.bashrc,然后执行source ~/.zshrc或者重新开一个终端。
如果你更希望用TeX Live自带的方式,也可以在2020版本中直接执行:
sudo tlmgr path add这条命令在Linux和macOS上通常会把TeX Live的bin目录软链到/usr/local/bin这类系统标准目录下,省得自己改PATH。改完之后重新打开终端或重启IDE,再执行which pdflatex确认路径已经切到2020的版本即可。
这里要插一句,IDE或者编辑器里的TeX插件,很多时候并不会继承终端里刚改的PATH。比如我在VS Code里遇到过,终端里which pdflatex正常,但F5编译时还是报同样错误,就是因为在插件启动时读到的还是旧环境。解决办法是重启IDE,或者在插件设置里手动指定2020版本pdflatex的完整路径。
4. 不同平台的实测差异:Linux、Windows和macOS的坑
同一个报错,在不同平台上的触发点和处理方式其实有挺大差异。下面把三个平台的实测情况单独拎出来说,省得你在自己平台上走弯路。
4.1 Linux:多版本共存是重灾区
Linux上最常见的场景就是两个TeX Live并存:一个是发行版仓库里的版本,另一个是用官方install-tl脚本装的。前者一般被放在/usr/bin下,后者通常被放在/usr/local/texlive/2020/bin/平台目录下。两个版本共存时,系统默认的PATH里/usr/bin通常排在前面,所以一顿操作猛如虎,调用的却是老版本。
我处理这种问题时的优先级是这样:先确认自己到底想用哪个版本。如果只想用官方2020版本,就把2020的bin目录放在PATH最前,然后重新登录。如果发行版自带的TeX Live已经严重影响你,也可以直接卸载:
sudo apt remove texlive-binaries这条命令适合Debian系系统,因为各种编译相关的包依赖它,卸载后如果其他软件确实需要,再单独安装需要的包。不过我更推荐的方式是不要动系统包,只靠PATH隔离,毕竟有些系统工具编译时确实需要依赖/usr/bin下的TeX二进制。
另外,在Linux服务器上排查时,还要留意环境变量的继承问题。如果报错来自某个CI任务、systemd服务或者cron脚本,你改的是自己shell里的PATH,那些守护进程根本不会读到。正确的做法是在服务配置或CI脚本里显式设置PATH和TEXMF相关变量,或者在启动命令中直接写pdflatex的绝对路径。
4.2 Windows:系统PATH与管理员权限
Windows上的问题有比较明显的平台特色。安装TeX Live 2020时,如果安装界面里没有勾选“添加到系统PATH”,那么命令行里直接执行pdflatex就会找不到,更别说编译工具了。
在Windows上解决这个报错,首先要确认where pdflatex的输出。如果来源是C:\texlive\2020\bin\windows,说明路径没问题,问题更可能在格式生成阶段。如果来源是C:\Program Files\MiKTeX\miktex\bin\x64,那么就是另一个发行版抢占了命令,你需要修改系统环境变量,把C:\texlive\2020\bin\windows放到Path列表的前面,或者干脆卸载MikTeX。
Windows还有一个特有的权限问题。fmtutil-sys这类系统级命令在修改文件时,必须以管理员身份运行。很多朋友直接在普通权限的PowerShell里执行,结果命令看起来跑了,实际上某些文件没能写入,格式文件还是缺失。我建议你右键以管理员身份打开终端或PowerShell,再执行:
fmtutil-sys --byfmt pdflatex另外,Windows安全软件有时会把web2c目录下的新生成文件当作风险文件处理。如果你发现每次重建完,下一次编译又报找不到格式文件,可以检查一下安全软件的隔离记录,并把TEXMFVAR对应的目录加入白名单。
4.3 macOS:/Library/TeX/texbin的坑
macOS上如果用MacTeX安装,日常使用的pdflatex通常通过/Library/TeX/texbin这个路径提供。这个目录里的可执行文件其实是符号链接,指向实际的/usr/local/texlive/2020/bin/universal-darwin目录。很多朋友不知道这一点,看到这个路径就懵了,不知道去哪里调整。
如果你的macOS上同时用Homebrew装过某些TeX组件,通过which pdflatex可能会发现,实际命中的是Homebrew的二进制,而不是/Library/TeX/texbin下的MacTeX版本。我遇到这种情况时,最稳定做法是在~/.zshrc里手动指定:
export PATH=/Library/TeX/texbin:$PATH然后执行hash -r清理shell的命令缓存,再验证版本。macOS上不同的TeX Live版本还存在用户级缓存叠加的问题,比如你用过TeX Live Utility做过更新,旧格式文件残留在~/Library/texlive/2020/texmf-var下,可能干扰新的重建结果。如果重建后依然报错,可以检查这个用户级目录,必要时删掉里面的web2c子目录,再重新生成。
5. 踩坑记录与高频问题速查
这些内容是平时文档里不怎么会写的,但我个人觉得,比命令本身更值钱。
5.1 高频问题对照表
| 现象 | 最常见原因 | 第一步处理 |
|---|---|---|
| 报错,但which pdflatex指向/usr/bin | 发行版自带旧版抢占PATH | 修改~/.bashrc或~/ .zshrc,把2020的bin放前面 |
| 报错,which指向2020,但kpsewhich找不到fmt | 安装中断,格式未生成 | sudo fmtutil-sys --byfmt pdflatex |
| 报错,kpsewhich能找到fmt | 二进制版本与fmt不匹配 | 检查PATH,清理用户级缓存后重建 |
| fmtutil-sys --all报Permission denied | 普通用户执行系统级命令 | 加sudo,或以管理员身份重开终端 |
| 重建后依然报错 | texmf-dist下有包损坏 | 执行tlmgr update --all,然后再次全量重建 |
| Windows下where正确但编译工具仍报错 | IDE或服务进程保留旧环境 | 重启IDE或重新登录系统 |
| fmt文件被安全软件隔离 | 杀毒软件误隔离web2c目录 | 在安全软件中添加白名单后重建 |
这套对照表我基本是贴在工位上的,遇到这类问题直接按行查,能省下不少时间。
5.2 我实际维护中踩过的几个坑
第一个坑是sudo和普通用户混用。有一次我在一台机器上排查,先用普通用户执行fmtutil,跑到一半权限不够失败了;又换成sudo执行成功,结果用户目录下残留了一堆root拥有的文件。等下次用普通用户编译时,系统尝试写入用户级缓存目录,又因为没有写权限报错。这种情况的根治办法是统一权限:系统级操作用sudo,用户级操作不带sudo;如果已经产生了root拥有的文件,直接sudo chown -R 用户名:组名 ~/.texlive2020,或者删掉用户级缓存目录让它重建。
第二个坑是CI环境里的连锁报错。当时一整批编译节点全部报这个错,排查了半天发现是网络安装TeX Live时使用的镜像源不完整,导致部分格式文件没有下载完全。后来改为从完整镜像安装,问题消失。所以如果你用在线安装方式,装完最好第一时间验证一下kpsewhich pdflatex.fmt是否有输出,没有的话尽早重跑安装或补全模块。
第三个坑是更新宏包后突然爆炸。某次我更新了几个常用宏包,第二天编译直接报错,一开始还以为是源码问题。后来才发现,宏包升级到新版本后,之前生成的fmt已经不适合新环境了,但系统不会自动重新生成。解决办法就是跑一遍sudo fmtutil-sys --all。从那以后我养成了一个习惯:但凡执行过tlmgr update,或者手动改动过texmf-dist目录下的内容,都会顺手重建一次格式,省得后续踩雷。
这个报错说到底并不复杂,核心就是“文件没生成”和“引擎找错地方”这两件事。我现在遇到它,已经养成固定流程:先which,再kpsewhich,再fmtutil-sys --byfmt。按这个顺序操作,大多数情况下三步内就能解决。如果你手头正好被这个报错卡住,建议从第二节的排查步骤开始试起,重点留意PATH对不对。希望这篇记录能帮你少走一些弯路。