为什么标题上有两个DATE(1) 的追踪过程

我们通过

man -aw date

得到

/usr/share/man/man1/date.1.gz

用zcat观察

zcat /usr/share/man/man1/date.1.gz |less

可以看到第一行是

.\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.47.3.
.TH DATE "1" "February 2017" "GNU coreutils 8.25" "User Commands"
.SH NAME
date \- print or set the system date and time
.SH SYNOPSIS
.B date
[\fI\,OPTION\/\fR]... [\fI\,+FORMAT\/\fR]
.br
.B date
......

这里 .TH DATE "1"便是DATE(1)的出处, 但为什两个呢? 继续挖

用grep过滤

 zcat /usr/share/man/man1/date.1.gz | grep 'DATE \"1\"'

搜索仅有一行输出

date1

查找man源码

$ which man
/usr/bin/man
$ dpkg --search /usr/bin/man
man-db: /usr/bin/man
$ apt-get source man-db

狂看一天之后, 发现man-db用的是pipeline,格式化和最终的输出调用的都是外部指令

/* Return pipeline to format file to stdout. */
static pipeline *make_roff_command (const char *dir, const char *file,
                    pipeline *decomp, const char *dbfilters,
                    char **result_encoding)
......                    

从这个函数的内容里, 发现主要是调用的roff/nroff/troff/groff这一组格式化命令来完成的.

创建一个小文件a.txt,包含如下内容

.TH MAN 7 2012-08-05 "Linux" "Linux Programmer's Manual"
.SH NAME
man \- macros to format man pages

用groff试下

$ groff -mandoc -T ascii a.txt

输出了以下内容

MAN(7)                     Linux Programmer's Manual                    MAN(7)



NAME
       man - macros to format man pages






Linux                             2012-08-05                            MAN(7)

果然是我想要的.

用strace查看调用过程

还是pipe调用....

下载groff源码

$ apt-get source groff

发现可以用-V参数查看pipeline命令

$ groff -V -mandoc -T ascii a.txt
troff -mandoc -Tascii a.txt | grotty

单独执行troff

troff -mandoc -Tascii a.txt

得到

x T ascii
x res 240 24 40
x init
p1
x X tty: sgr 0
x font 1 R
f1
s10
V40
H0
md
DFd
tMAN(7)
h504
tLinux
wh24
tProgrammer's
wh24
tManual
h480
tMAN(7)
......

从这里可以看到在a.txt里只出现一次的MAN(7)被输出了两次.

troff源码分析

troff 源码也在groff包里.
搜索源码并没有找到直接处理.TH标记的地方, 发现troff与man的宏有关.

troff与宏指令man macros

通过以下指令得到troff在运行过程中打开过的.tmac宏文件

$ strace troff -mandoc -Tascii ~/a.txt 2>&1 |grep -v such | grep tmac

输出如下

open("/usr/share/groff/1.22.3/tmac/troffrc", O_RDONLY) = 3
open("/usr/share/groff/1.22.3/tmac/composite.tmac", O_RDONLY) = 4
open("/usr/share/groff/1.22.3/tmac/fallbacks.tmac", O_RDONLY) = 4
open("/usr/share/groff/1.22.3/tmac/tty.tmac", O_RDONLY) = 4
open("/usr/share/groff/1.22.3/tmac/hyphen.us", O_RDONLY) = 4
open("/usr/share/groff/1.22.3/tmac/hyphenex.us", O_RDONLY) = 4
open("/usr/share/groff/1.22.3/tmac/papersize.tmac", O_RDONLY) = 4
open("/usr/share/groff/1.22.3/tmac/pspic.tmac", O_RDONLY) = 4
open("/usr/share/groff/1.22.3/tmac/andoc.tmac", O_RDONLY) = 3
open("/usr/share/groff/1.22.3/tmac/troffrc-end", O_RDONLY) = 3
open("/usr/share/groff/1.22.3/tmac/an-old.tmac", O_RDONLY) = 4
open("/usr/share/groff/1.22.3/tmac/devtag.tmac", O_RDONLY) = 5
open("/usr/share/groff/1.22.3/tmac/an-ext.tmac", O_RDONLY) = 5
open("/usr/share/groff/site-tmac/man.local", O_RDONLY) = 5

在相应目录下只找到少处和.TH处理有关的片断

/usr/share/groff/1.22.3/tmac$ grep -w TH -R *
andoc.tmac:.  als TH reload-man
andoc.tmac:.  rm TH
andoc.tmac:\\*[TH]\\
andoc.tmac:.als TH reload-man
andoc.tmac:.\" dummy equation macros -- eqnrc is read before .TH or .Dd is parsed
an-old.tmac:.\" If you need to add things to TH, use `.am1 TH'.
an-old.tmac:.\" .TH title section extra1 extra2 extra3
an-old.tmac: TH

.tmac文件格式在这里有详细的介绍:

doc.tmac 主手册宏包
mdoc.tmac 对 doc.tmac调用的封装
mdoc/doc-common 共用字符串,定义,与印刷输出相关内容.
mdoc/doc-nroff 用于 TTY设备输出的定义.
mdoc/doc-ditroff 用于其它设备输出的定义
mdoc.local 本地补充与客户化相关.
andoc.tmac 如果你搞不清是该用 -mdoc 还是 -man 包时就用这个.

真相大白

最终在an-old.tmac层层定义的宏里找到了这行:

259 .de1 PT
260 .  tl '\\*[an-title](\\*[an-section])'\\*[an-extra3]'\\*[an-title](\\*[an-section])'
261 ..

编辑保存, 果然如此

.  tl '\\*[an-title](\\*[an-section])'\\*[an-extra3]' [Eureka!] \\*[an-title](\\*[an-section])'

截图留念

man date

这里有man-db有相关的中文文档.


Yujiaao
12.7k 声望4.7k 粉丝

[链接]