TeX Live 里的大多数命令并不排版文档,而是用来读文档。texdef 让你看清某条命令究竟是怎么定义的;chktex 与 lacheck 指出源文件里的习惯性毛病;texfot 把上千行日志压到几行;texcount 统计词数;texloganalyser 从 .log 中挖出你要的行。LaTeX 工作中真正耗时的从来不是排版本身,而是弄清楚为什么没成功,所以花一小时熟悉这一角很划算。用 texdoc 打开宏包手册属于另一页的范围;这里讲其余部分,最后再放上处理成品 PDF 的 Ghostscript。
texdef — 这条命令到底是怎么定义的
当重定义彼此打架时,想知道当前生效的到底是哪一个定义,最快的办法是直接问引擎。敲 latexdef \section,回来的是 \long macro:->\@startsection {section}{1}{\z@ }{-3.5ex \@plus -1ex \@minus -.2ex}{2.3ex \@plus .2ex}{\normalfont \Large \bfseries }——是真正的值,而且是你这台机器上的值。想在加载某宏包的前提下查看,就加 -p:latexdef -p amsmath \tfrac 返回 \genfrac {}{}{}1,一眼就能看出 \tfrac 不过是 \genfrac 的一层薄包装。传入不存在的名字,回答只有干巴巴的 undefined。作者是 Martin Scharrer,TeX Live 2024 中的版本是 1.9(2020/09/27)。
这里出现了与 dvipdfmx 相同的把戏:latexdef 是指向 texdef 的符号链接,程序会根据自己被调用时的名字决定加载哪个格式——texdef 表示 plain TeX,latexdef 表示 LaTeX。所以裸的 texdef \LaTeX 会回答 undefined,这并不是缺陷。同样的意思也可以用 -t latex 或 -t lualatex 明确写出。更有用的是定位类选项。-F 会报出命令定义所在之处:latexdef -p booktabs -F \toprule 回答 \toprule first defined in "/usr/local/texlive/2024/texmf-dist/tex/latex/booktabs/booktabs.sty".。而 -l 会列出某宏包提供的用户级命令——想在翻手册之前先看看有哪些东西时很方便。
latexdef \section # the definition in force right now
latexdef -p amsmath \tfrac # with a package loaded
latexdef -p booktabs -F \toprule # which file defined it
latexdef -p geometry -l # the user-level commands a package adds
latexdef -c beamer \frametitle # inside a different document classchktex 与 lacheck — 两个检查器看的东西并不相同
两者抓的都是「能编译过、版面却不对」这一类问题,但各有所长。chktex 看的是排版习惯。 把同一份稿子交给它,会得到带编号的意见,例如 Warning 26 in lint.tex line 4: You ought to remove spaces in front of punctuation. 与 Warning 44 in lint.tex line 5: User Regex: 1:Capitalize before references.,并用 ^ 指出出问题的那一列。有编号就意味着可以屏蔽:若要求把直双引号换成 TeX 引号的第 18 条碍事,就用 chktex -n18。lacheck 看的是结构对应。 当环境、花括号或数学定界符不匹配时,它会把两端连同行号一起给出,以 <- 与 -> 成对标示。用一份故意弄坏的文件测试时,它先打出 "broken.tex", line 5: <- unmatched "\end{enumerate}",紧接着是 "broken.tex", line 3: -> unmatched "\begin{itemize}"——比引擎更早,形式也可读得多。
若要放进 CI,有一个决定性的差别必须知道:lacheck 无论发现什么都返回退出码 0。 上面那份坏文件——两处环境不匹配、一个数学式没闭合——返回的仍是 0。也就是说,它不能用来卡构建,你必须自己捕获它的标准输出再判断。相反,chktex -q 在打印了警告的每一次都返回 2,文件干净时返回 0。能用来把关的是它。还有一点:本机 TeX Live 2024 的 macOS 版 chktex 是用 POSIX 扩展正则编译的,而其默认规则中有一条用了 Perl 式的先行断言,于是每次运行都会先打出一行以 chktex: WARNING -- Compilation of regular expression 开头的消息。它没有实际危害,但在 CI 日志里配置为忽略会清净不少。
chktex -q paper.tex # exit 2 when it prints warnings, 0 when clean
chktex -q -n18 -n26 paper.tex # mute the quotation-mark and punctuation rules
lacheck paper.tex # unmatched groups, shown as a <- and -> pair
lacheck paper.tex | tee lacheck.out # its exit code is always 0, so read this想理解这些工具的性格,lacheck 的 man 页 BUGS 一节值得一读。它承认自己会被高级宏搞糊涂、会被简单宏骗过,承认它不喜欢 TeX,也没有办法逐条关闭警告——并自称「at best a crude approximation」。也许正是这份坦率,让 Kresten Krab Thorup 在 1990 年代前期写下、经 Per Abrahamsen 修改的这个小工具至今仍随 TeX Live 一同发行。chktex 那边也依旧在启动横幅上挂着 Copyright 1995-96 Jens T. Berger Thielemann.。近三十年前的工具,对今天写下的稿子仍能提出有用的意见——这也是 LaTeX 世界的速度。
texfot — 把 24 行日志变成 4 行
没人读 TeX 输出的原因很简单:量太大。texfot 会代你启动引擎,并只放行值得注意的那些行。本机上,一份用了 lipsum 的单页文档在裸 pdflatex 下滚过 24 行;同样的运行交给 texfot pdflatex 只剩 4 行。留下来的是引擎的版本行、Overfull \hbox (122.18651pt too wide) detected at line 4,以及 Output written on over.pdf (1 page, 19010 bytes).。而且什么都没被丢掉:未过滤的完整输出会被 tee 到一个临时文件,需要时可以事后再读(--tee= 改变去处,--tee=/dev/null 则关掉)。退出码原样透传自引擎,因此塞进 latexmk 或 CI 也不会破坏判定——在有错误的文档上测试时,它与裸 pdflatex 一样返回 1。作者是 Karl Berry。
texfot pdflatex paper.tex
# texfot: invoking: pdflatex paper.tex
# This is pdfTeX, Version 3.141592653-2.6-1.40.26 (TeX Live 2024)
# Overfull \hbox (122.18651pt too wide) detected at line 4
# Output written on paper.pdf (1 page, 19010 bytes).
texfot --tee=/dev/null lualatex paper.tex # do not keep the full copy
# .latexmkrc: wrap the engine
$pdflatex = 'texfot pdflatex %O %S';texcount 与 texloganalyser — 数词数,挖日志
投稿要求写着「正文不超过 8,000 词」时,wc -w 派不上用场:它会把 \usepackage 和各种宏名都算进去。texcount(TeXcount 3.1.1,2018 年 10 月)理解 LaTeX 语法并据此计数,把正文、标题、图题分开报告,连公式有几个都分开列出。加上 -brief 就压缩成一行,例如 2+0+0 (0/0/0/0) File: over.tex。另一个 texloganalyser(Thomas van Oudenhove,version 0.11,BSD 许可证)则是事后挖掘已写出的 .log 的工具:-o 只取 overfull 盒子,-r 只取交叉引用警告,-i 取载入的图像,-s 取用到的 .sty 与 .cls。texfot 是边跑边筛,它是从跑完的日志里挖——两者用途不重叠,同时留着并无坏处。
texcount -brief paper.tex # words in text + headers + captions
texcount -inc -sum paper.tex # follow \input and \include, one total
texloganalyser -o paper.log # only the overfull boxes
texloganalyser -r paper.log # only the reference warnings
texloganalyser -s paper.log # which .sty and .cls were actually loadedGhostscript — 合并 PDF,以及「压缩」不起作用的情形
gs 是 PostScript 与 PDF 的解释器,也是 ps2pdf 底层跑的东西。给它 -sDEVICE=pdfwrite,它就会写出 PDF,于是从 PostScript 转换、合并多个 PDF、抽取页面,各用一行就能解决。合并最为简单:按顺序列出输入即可。本机上,一份 7,940 字节与一份 6,865 字节的 PDF 合成了一册 12,543 字节的文件。另外,gs 并不属于 TeX Live,它是 MacTeX 等安装程序另行装上的 Ghostscript,本机版本为 10.03.0。
gs -q -sDEVICE=pdfwrite -dNOPAUSE -dBATCH \
-sOutputFile=merged.pdf front.pdf body.pdf
gs -q -sDEVICE=pdfwrite -dNOPAUSE -dBATCH \
-dFirstPage=3 -dLastPage=8 -sOutputFile=extract.pdf paper.pdf
gs -q -sDEVICE=pdfwrite -dPDFSETTINGS=/ebook -dNOPAUSE -dBATCH \
-sOutputFile=small.pdf big.pdf这里要纠正一个流传很广的说法:-dPDFSETTINGS 并不是「压缩 PDF 的开关」。 这些预设主要决定的是图像重采样的策略,分辨率下限从 /screen(面向屏幕)依次提高到 /ebook、/printer、/prepress。因此不含位图图像的文档根本无从缩小。本机实测:一份纯文字、6,865 字节的 PDF,经 /screen 变成 7,310,/ebook 变成 7,309,/prepress 变成 7,302,/printer 更是 9,869 字节——四者都比原文件更大,因为剩下的只有重建文件的开销。这个选项真正发挥作用的场合,是塞满照片与扫描件的文档。而且无论哪种情况,通过前后都要用眼睛核对:字体嵌入、链接、书签与页面尺寸。 pdfwrite 会重建 PDF,过程中可能丢掉看不见的信息。
按疑问查命令
| 命令 | 它回答的问题 |
|---|---|
latexdef NAME | 这条命令现在是怎么定义的;加上 -F 还能知道是哪个文件定义的 |
chktex -q | 稿子里有没有排版上的坏习惯;它返回的状态可用来卡 CI |
lacheck | 环境与括号是否成对匹配;但退出码永远是 0 |
texfot ENGINE FILE | 这一次的日志里,真正该看的是哪几行 |
texcount -brief | 正文有多少词,不把宏和 \usepackage 算进去 |
texloganalyser -o | 从已有的 .log 中只挖出 overfull 盒子或引用警告 |
gs -sDEVICE=pdfwrite | 合并 PDF、抽取页面,或为含大量图像的 PDF 减重 |