构建工具

人们说「装好了 LaTeX」,真正落到硬盘上的并不是一个程序,而是一个塞满命令行工具的目录。在手边这台装有 TeX Live 2024 的机器上,该目录里排着将近 490 条命令,而平常写稿时真正会敲的只有一两条。剩下的四百八十来条,是发行版为「文档突然不听话的那一天」准备的后备:问宏包究竟装在哪里的 kpsewhich、打开它的说明书的 texdoc、补装缺失部件的 tlmgr、裁掉插图白边的 pdfcrop。本页把这批后备按「它能回答什么问题」重新排成一张地图。至于坐在最上层的构建驱动(latexmkllmkarara)另有专页,这里只谈它们下面那一层。

「在我这儿能编译啊」——这时先敲 kpsewhich

同一份 .tex,在你机器上通过,在合作者机器上却停住。这类症状几乎全都是「文件在哪里」的问题,而 kpsewhich 一行就能定案。这条命令之所以权威,是因为它并非自行搜索:TeX Live 2024 的每个引擎都链接了文件查找库 kpathsea(本版为 6.4.0),而 kpsewhich 就是把同一个库包装成的独立命令。它打印的路径,正是引擎将要打开的那个文件,而不是猜测。如果 kpsewhich amsmath.sty 什么都不输出,说明该宏包在这台机器上根本不存在,编译时会出现 ! LaTeX Error: File amsmath.sty not found.。若它确实输出了内容,那问题就不是「缺失」,而是「看到的是另一个」。

terminal
kpsewhich amsmath.sty
# /usr/local/texlive/2024/texmf-dist/tex/latex/amsmath/amsmath.sty

kpsewhich article.cls          # classes are found the same way
kpsewhich --all texmf.cnf      # every match, in search order
kpsewhich --progname=xelatex --format=tfm cmr10.tfm

加上 --all,得到的就不只是第一个命中,而是按搜索顺序排列的全部结果。比「文件缺失」更棘手的事故正藏在这里。TeX 只用第一个命中,于是几年前复制进稿件目录的旧 .sty 会悄悄遮住发行版里的新版本。日志中看不出任何异常,只是行为随机器而异。跑一次 kpsewhich --all,同一个名字出现两次,问题就现形了。kpsewhich 也不限于 .sty:文档类(article.cls)、字体度量文件、配置文件都走同一套查找机制。若不同引擎的搜索路径有别,可以用 --progname=xelatex 换个身份去问;用 --format=tfm 指明文件类别则能收窄范围(类别清单见 kpsewhich --help-formats)。

--var-value--show-path:读出引擎所相信的配置

kpsewhich --var-value=TEXMFHOME 只打印一个配置变量,kpsewhich --show-path=tex 则把查找 .tex 时要走的目录序列整个印出来。前者回答「在哪里」,后者回答「按什么顺序」。掌握这两条,就不必再依赖文档里的泛泛之谈,而能读出眼前这台机器实际采用的值。最要紧的是下面四个变量——TeX 的搜索树按角色划分:个人、站点、发行版、生成物。顺序同样有含义:个人树排在发行版之前,正因如此,你自己的 .sty 才能覆盖标准版本。

变量角色取值示例(TeX Live 2024 / macOS)
TEXMFHOME个人专属树;唯一无需管理员权限即可写入的位置~/Library/texmf
TEXMFLOCAL全机共享的附加内容;跨年度升级仍保留/usr/local/texlive/texmf-local
TEXMFDIST发行版安装的本体;不要手工编辑/usr/local/texlive/2024/texmf-dist
TEXMFVAR生成物存放处;格式文件与字体缓存堆积于此~/Library/texlive/2024/texmf-var
terminal
kpsewhich --var-value=TEXMFHOME
# /Users/you/Library/texmf

kpsewhich --show-path=tex      # the whole ordered search list
kpsewhich --expand-var='$TEXMFDIST/tex/latex'

自制的 .sty 放在哪里——个人 texmf 树

自己写的 .sty.cls,或从 CTAN 手工下载的宏包,都归入 TEXMFHOME。不要猜路径,直接问 kpsewhich --var-value=TEXMFHOME——macOS 上的 MacTeX 通常回答 ~/Library/texmf,Linux 的 TeX Live 则通常回答 ~/texmf,各环境不同。这里有个坑:这个目录一开始往往并不存在。 因为 kpsewhich 报告的是「配置中的位置」,而非「实际存在的位置」。所以第一步是自己把它打印出的路径建出来。目录内部遵循 TDS(TeX Directory Structure):LaTeX 宏包放在 TEXMFHOME/tex/latex/<名称>/<名称>.sty

terminal
mkdir -p "$(kpsewhich --var-value=TEXMFHOME)/tex/latex/mystyle"
cp mystyle.sty "$(kpsewhich --var-value=TEXMFHOME)/tex/latex/mystyle/"
kpsewhich mystyle.sty          # found immediately, no texhash needed

texhashmktexlsr——同一个程序的两个名字

texhashmktexlsr 做的是同一件事——更准确地说,它们就是同一个文件。翻开 TeX Live 2024 的 bin 目录会发现,texhash 是指向 mktexlsr 的符号链接。两者都用来重建文件名数据库 ls-R。发行版的目录树极其庞大:本机上仅 texmf-dist/ls-R 一个文件就超过 5 MB。每次都遍历磁盘实在太慢,于是系统侧的树改为只查 ls-R,代价是每当添加文件就得重建数据库。所以「我往 TEXMFLOCAL 里放了东西却找不到」的答案是 sudo mktexlsr。相反,上一节的 TEXMFHOME 每次都会去磁盘上看,因此放进去的 .sty 当场就能找到——实际建一个再问 kpsewhich,一次都不用跑 texhash 就有答案。这也是推荐使用个人树的理由之一。

texdoc——宏包的说明书早已在你机器里

敲下 texdoc geometrygeometry 宏包的 PDF 手册就会在阅读器中打开。全程不需要网络——宏包从 CTAN 送来时,说明书本来就一并附上了。这条命令的价值正在于此:你实际装着的那个版本的手册,比搜索引擎翻出来的陈年教程准确得多。同名文档可能不止一份,拿不准时用 texdoc -l geometry 列出候选;在本机上会同时列出英文的 geometry.pdf 和德文的 geometry-de.pdf。加上 -s(showall)会把关联较弱的结果也一并捞出,加上 -M 则给出机器可读的列表。TeX Live 2024 附带的是 Texdoc 4.1(2024-03-10),版权行上列着 Manuel Pégourié-Gonnard、Takuto Asakura 与 TeX Live Team。

terminal
texdoc geometry            # open the manual
texdoc -l geometry         # list every candidate first
texdoc texdoc              # the manual for texdoc itself

tlmgr install / update / info,以及「TeX Live 2024 is frozen」的含义

tlmgr 是 TeX Live 的宏包管理器,日常会用到的有三条:tlmgr info NAME 查询某个宏包,tlmgr install NAME 安装它,tlmgr update --self --all 把整体更新到最新。其中 info 的输出最实用——installed: Yes 说明是否已装,revision: 说明是哪一版,collection: 说明它属于哪个集合。这里有一个不知道就会白白耗掉半天的事实:命令名与 TeX Live 的宏包名未必一致。 例如 tlmgr info llmk 会先回 tlmgr: cannot find package llmk,随后自动改用描述与文件名去搜索,给出 light-latex-makellmk 这条命令并不住在同名的宏包里。

terminal
tlmgr info amsmath             # installed? which revision? which collection?
sudo tlmgr install siunitx     # a system-wide tree needs root
sudo tlmgr update --self --all
tlmgr --version                # also prints which installation is in use

另一件初次见到会吓一跳的,是冻结通知。在 TeX Live 2024 上执行 tlmgr,多数子命令会先打印 TeX Live 2024 is frozenand will no longer be routinely updated.。这是告知,不是错误。它只是在陈述 TeX Live 的运作方式:新的年度版发布后,上一年度的仓库便停止常规更新。命令本身照常在下面运行——tlmgr info amsmath 打完这段横幅后,仍会规规矩矩地给出完整信息。因此对策不是「再跑一遍」,而是「装上新的年度版」。此外,在 MacTeX 这类标准安装中,/usr/local/texlive/2024/texmf-dist 归 root 所有,所以 tlmgr installtlmgr update 需要 sudo。在没有管理员权限的机器上,与其在这里较劲,不如按上一节把文件手工放进 TEXMFHOME,往往更快。

pdfcrop——裁掉插图 PDF 的白边

执行 pdfcrop figure.pdf,会逐页计算并裁去白边,输出 figure-crop.pdf。用得上它的场合是:把 TikZ 图做成了独立文件,或者别的软件导出的 PDF 是「A4 正中一小张图」的样子。若原样 \includegraphics,贴进去的就不是图而是纸。想刻意留一点边距时,可用 --margins "5 5 5 5" 指定四边,单位为 bp(big point)。坑在依赖:pdfcrop 自己并不裁剪——默认要调用 Ghostscriptgs,可用 --gscmd 更改)和一个 TeX 引擎。绝大多数「装了 pdfcrop 却跑不动」的反馈,根源都是缺少 Ghostscript。TeX Live 2024 附带的是 pdfcrop 1.42(2023/04/15,Heiko Oberdiek 编写)。

terminal
pdfcrop figure.pdf                       # -> figure-crop.pdf
pdfcrop --margins "5 5 5 5" figure.pdf   # keep 5bp on every side
pdfcrop --luatex figure.pdf              # drive lualatex instead of pdftex

查看成品 PDF 的内部——pdftotext 并不是 TeX 的命令

想从成品 PDF 中抽取文字时,多数教程都会搬出 pdftotext。但要注意:pdftotext 并不属于 TeX Live。 把 TeX Live 2024 的 bin 目录从头翻到尾也找不到它;这台 Mac 上的那一份是 Homebrew 的 poppler 装的。pdfinfo 同理。用当然可以用,只是要明白:装好了 TeX 却出现 pdftotext: command not found,并不是坏了,它本来就是另一套软件。TeX Live 这边有的是 pdftosrc,其用法行写作 pdftosrc <PDF-file> [<stream-object-number>],是从 PDF 中取出流对象的工具。日常排查时,与其盯着 PDF,不如先读 .log 更快;而把日志精简的 texfot、处理 PDF 的 Ghostscript、输出矢量图的 dvisvgm 等周边工具,另有专页整理。

按症状查命令

命令什么时候用它
kpsewhich NAME.sty宏包找不到,或找到的那一个看着可疑
kpsewhich --all NAME.sty行为随机器而异——查一查是不是有旧副本遮住了正主
kpsewhich --var-value=TEXMFHOME想确认自制文件该放在哪里
kpsewhich --show-path=tex怀疑的是搜索顺序本身
texdoc NAME想不起某个选项名,或者当前没有网络
tlmgr info NAME想确认它装没装、装的是哪一版
sudo tlmgr install NAME确实没装,而且你有管理员权限
sudo mktexlsr已把文件放进系统侧的树,却仍然找不到
pdfcrop插图 PDF 大半是白边,贴进去就变小

真正值得养成的,是提问的顺序。一旦觉得不对劲,先用 kpsewhich 查位置、用 texdoc 查规格,再去问人:这两条既不需要网络也不需要管理员权限,几秒就有答案。只有当它们表明东西确实缺失,才轮到 tlmgr 出场;而放文件拿不定主意时,就选 TEXMFHOME。把引擎跑几遍、按什么顺序跑自动化起来的那一层(latexmk 之类),和这一层「找、读、装」是两回事;前者调得再细,只要后者不牢靠,各机器之间的出入就不会消失。