nl 命令详解与参数实测(Ubuntu 22.04)
nl 全参数实测:默认 -b t 跳空行、-b a/n、-n ln/rn/rz 对齐补零、-w 宽度、-s 分隔符、-b p 正则挑行、三区逻辑页与 -d 自定义、与 cat -n 差异,Ubuntu 22.04 实测。
nl 命令详解与参数实测(Ubuntu 22.04)
目标:全面掌握 nl 的行号控制能力——默认
-b t跳过空行编号、-b a/-b n编号策略、-n ln/rn/rz三种对齐格式、-w行号宽度、-s行号分隔符、-b p正则挑选编号行、\:/\:\:/\:\:\:三区逻辑页与-d自定义分隔符、与cat -n的差异,全部命令与回显已在 Ubuntu 22.04(nl 属 GNU coreutils 8.32)实测通过。
一、环境信息
本节记录实测环境与演示数据,便于读者对照复现。nl 与 cat -n 都能加行号,但 nl 的可调维度多得多——谁编号、怎么对齐、多宽、用什么分隔、按什么分区,全部可配。环境信息里同时列出演示目录与测试文件。
- 操作系统:Ubuntu 22.04.5 LTS(x86_64)
- nl 版本:GNU coreutils 8.32(软件包 coreutils 8.32-4.1ubuntu1.4,见 2.3 实测)
- 演示目录:
/root/demo/nl/ - 测试数据:含空行与数字行的
poem.txt(6 行,2 个空行)、三区结构文件page.txt(含\:\:\:/\:\:/\:分隔行)、自定义分隔符文件page2.txt(含%:行)
二、命令概览
2.1 语法与测试数据准备
帮助原文给出的语法是 nl [OPTION]... [FILE]...,职责一句话:Write each FILE to standard output, with line numbers added(输出文件内容并加行号)。nl 按「逻辑页」工作:一个文件可由分隔行切成 header(页眉)/ body(正文)/ footer(页脚)三区,各区可独立决定编号策略;帮助原文给出的默认参数是 -bt -d'\:' -fn -hn -i1 -l1 -n'rn' -s<TAB> -v1 -w6——即正文非空行编号、行号右对齐宽 6、编号后接制表符、从 1 起步。
准备演示数据——含空行与数字开头的 poem.txt、三区结构的 page.txt(heredoc 写入 \: 字面分隔行)、%: 分隔的 page2.txt:
cat > /root/demo/nl/poem.txt <<EOF
alpha line
bravo line
charlie line
delta line
123 numeric line
EOF
cat > /root/demo/nl/page.txt <<EOF
intro line
\:\:\:
header line
\:\:
body line
\:
footer line
EOF
printf "p1\n%%:\np2\n%%:\np3\n" > /root/demo/nl/page2.txt
用 cat -A 确认 page.txt 的分隔行是真实的 \:\:\:、\:\:、\: 三种字面行($ 表明各行以换行结尾):
$ cat -A /root/demo/nl/page.txt
intro line$
\:\:\:$
header line$
\:\:$
body line$
\:$
footer line$
验证数据就绪:
$ ls -l /root/demo/nl
total 12
-rw-r--r-- 1 root root 60 Oct 15 07:45 page.txt
-rw-r--r-- 1 root root 15 Oct 15 07:45 page2.txt
-rw-r--r-- 1 root root 65 Oct 15 07:45 poem.txt
2.2 --help 全量回显
nl 的参数覆盖编号策略、格式、宽度、分隔符、逻辑页,帮助原文完整收录:
Usage: nl [OPTION]... [FILE]...
Write each FILE to standard output, with line numbers added.
With no FILE, or when FILE is -, read standard input.
Mandatory arguments to long options are mandatory for short options too.
-b, --body-numbering=STYLE use STYLE for numbering body lines
-d, --section-delimiter=CC use CC for logical page delimiters
-f, --footer-numbering=STYLE use STYLE for numbering footer lines
-h, --header-numbering=STYLE use STYLE for numbering header lines
-i, --line-increment=NUMBER line number increment at each line
-l, --join-blank-lines=NUMBER group of NUMBER empty lines counted as one
-n, --number-format=FORMAT insert line numbers according to FORMAT
-p, --no-renumber do not reset line numbers for each section
-s, --number-separator=STRING add STRING after (possible) line number
-v, --starting-line-number=NUMBER first line number for each section
-w, --number-width=NUMBER use NUMBER columns for line numbers
--help display this help and exit
--version output version information and exit
Default options are: -bt -d'\:' -fn -hn -i1 -l1 -n'rn' -s<TAB> -v1 -w6
CC are two delimiter characters used to construct logical page delimiters;
a missing second character implies ':'.
STYLE is one of:
a number all lines
t number only nonempty lines
n number no lines
pBRE number only lines that contain a match for the basic regular
expression, BRE
FORMAT is one of:
ln left justified, no leading zeros
rn right justified, no leading zeros
rz right justified, leading zeros
GNU coreutils online help: <https://www.gnu.org/software/coreutils/>
Report any translation bugs to <https://translationproject.org/team/>
Full documentation <https://www.gnu.org/software/coreutils/nl>
or available locally via: info '(coreutils) nl invocation'
💡 说明:两个先划的重点——其一,STYLE 有四种(a 全编 / t 只编非空行 / n 不编 / pBRE 按正则挑行),
-b/-h/-f三区各配一份;其二,-d CC的 CC 是两个分隔字符,帮助原文明确 "a missing second character implies ':'"(缺省第二字符补冒号),这条在 4.1 的-d实测里体现。
2.3 版本确认
确认 nl 的归属与版本:
$ nl --version | head -1
nl (GNU coreutils) 8.32
输出 nl (GNU coreutils) 8.32,即本文全部回显均来自 coreutils 8.32 的实现。
三、参数逐项实测
3.1 默认输出:-b t 跳过空行编号
先看 poem.txt 原始内容(6 行,其中第 3、5 行是空行,末行以数字开头):
$ cat /root/demo/nl/poem.txt
alpha line
bravo line
charlie line
delta line
123 numeric line
不带参数执行 nl(即默认 -b t,只给非空行编号):
$ nl /root/demo/nl/poem.txt
1 alpha line
2 bravo line
3 charlie line
4 delta line
5 123 numeric line
三个观察点:其一,空行不占号——两行内容行之间隔着一个空行,号从 2 直接跳 3;其二,空行输出为 6 列空格(行号区宽度仍在,只是空白);其三,数字开头的 123 numeric line 照常编号为 5(它也是非空行)。默认的行号区宽 6、右对齐,编号后接一个制表符再接内容。
💡 提示:
-b t里的 t 取自 text(只编正文行),是排版行业「正文计数」的传统;要让空行也占号,见 3.2 的-b a。
3.2 -b a 空行也编号 / -b n 全不编号
-b a(number all lines)给所有行编号,空行也占号:
$ nl -b a /root/demo/nl/poem.txt
1 alpha line
2 bravo line
3
4 charlie line
5
6 delta line
7 123 numeric line
与 3.1 对比:同样的 6 行文件,这里编到 7——空行领走了 3 号和 5 号。-b n(number no lines)则全不编号,行号区输出等宽空格:
$ nl -b n /root/demo/nl/poem.txt
alpha line
bravo line
charlie line
delta line
123 numeric line
行号区(6 列)被空格占据,内容整体右移。-b n 单看等价于「缩进版 cat」,但它的意义在组合:-h a -b n -f n 这类「各区不同策略」的搭配离不开 n 这个选项。
💡 提示:三种 STYLE 的记忆口诀——a=all、t=text(非空)、n=none;第四种
pBRE见 3.6 实测。
3.3 -n ln/rn/rz:行号对齐与补零
-n FORMAT 控制行号的对齐方式,帮助原文列出三种。-n ln(左对齐、无前导零):
$ nl -n ln /root/demo/nl/poem.txt
1 alpha line
2 bravo line
3 charlie line
4 delta line
5 123 numeric line
行号贴左、右侧补空格到 6 列。-n rn(右对齐、无前导零):
$ nl -n rn /root/demo/nl/poem.txt
1 alpha line
2 bravo line
3 charlie line
4 delta line
5 123 numeric line
与 3.1 默认输出一致——帮助原文的默认参数里就写着 -n'rn'。-n rz(右对齐 + 前导零):
$ nl -n rz /root/demo/nl/poem.txt
000001 alpha line
000002 bravo line
000003 charlie line
000004 delta line
000005 123 numeric line
行号补足 6 位:000001~000005。三种格式覆盖了左对齐、右对齐、补零右对齐的全部组合,配合 -w 可进一步定宽。
💡 提示:rz 的补零格式适合与等宽日志对齐(
000001比1在等宽字体下更整齐),也便于按字典序排序行号字符串。
3.4 -w:行号区宽度
-w N 调整行号区列数。-n rz -w 3 把行号压到 3 列:
$ nl -n rz -w 3 /root/demo/nl/poem.txt
001 alpha line
002 bravo line
003 charlie line
004 delta line
005 123 numeric line
行号区从 6 列缩到 3 列,内容跟着左移。-n rn -w 1 极限压到 1 列(行号最大 5,够用):
$ nl -n rn -w 1 /root/demo/nl/poem.txt
1 alpha line
2 bravo line
3 charlie line
4 delta line
5 123 numeric line
1 列行号 + 制表符分隔。注意 -w 只影响行号区宽度,行号超出宽度时(如 -w 1 但有第 10 行)coreutils 会按实际位数输出,不会截断数字。
💡 提示:默认 6 列是为兼容传统打印分页(行号区 + 制表符 + 内容);日常终端展示
-w 3 -s ' '更紧凑,见 3.5。
3.5 -s:行号与内容的分隔符
默认行号后接制表符,-s STRING 换成任意字符串。-s " | ":
$ nl -s " | " /root/demo/nl/poem.txt
1 | alpha line
2 | bravo line
3 | charlie line
4 | delta line
5 | 123 numeric line
行号、空格、竖线、内容依次排列,空行处也输出等宽的 | 区(5 列号区 + 分隔符)。换成 ") " 更像列表样式:
$ nl -s ") " /root/demo/nl/poem.txt
1) alpha line
2) bravo line
3) charlie line
4) delta line
5) 123 numeric line
-s 与 -w 组合可以做出「紧凑行号列表」:nl -w 3 -s ' ' 即 3 列行号 + 单空格分隔。
💡 提示:
-s的字符串支持多字符(帮助原文:add STRING after line number);想把行号变成1.样式就用-s ". "。
3.6 -b pBRE:按正则挑选编号行
STYLE 的第四种 pBRE:只有匹配基本正则的行才编号。给 -b 传 p^[[:alpha:]](只编以字母开头的行):
$ nl -b "p^[[:alpha:]]" /root/demo/nl/poem.txt
1 alpha line
2 bravo line
3 charlie line
4 delta line
123 numeric line
对比 3.1 默认输出:前五行完全一致,唯一差异在末行——123 numeric line 以数字开头、不匹配 ^[[:alpha:]],行号区只输出空格。编号只在匹配行之间连续递增(1~5),未匹配行不打断序号。
💡 提示:
-b p常用于「只给代码行编号、跳过注释/空段」的排版需求;BRE 语法与 grep 默认一致,正则请整体作为一个参数传入(如上例的引号),避免 shell 展开干扰。
四、实战示例
4.1 逻辑页三区与 -d 自定义分隔符
nl 的独门机制是逻辑页:文件里的特殊分隔行把内容切成 header(页眉)/ body(正文)/ footer(页脚)三区,进入新区时编号自动从 -v 起点重新计。分隔行的字符对由 -d CC 定义,默认是 \:——帮助原文说明两个字符的三种组合:\:\:\: 开启 header、\:\: 开启 body、\: 开启 footer。
用准备好的 page.txt(结构见 2.1:intro line → \:\:\: → header line → \:\: → body line → \: → footer line)实测默认 nl:
$ nl /root/demo/nl/page.txt
1 intro line
header line
1 body line
footer line
逐行解读:intro line 在初始 body 区,编号 1;\:\:\: 分隔行本身输出为空行,其后的 header line 进入 header 区,默认 -h n 不编号(行号区输出空格);\:\: 后的 body line 进入新 body 区,编号重新从 1 计;\: 后的 footer line 进入 footer 区,默认 -f n 不编号。再用 -h a -f a 让页眉页脚也编号:
$ nl -h a -f a /root/demo/nl/page.txt
1 intro line
1 header line
1 body line
1 footer line
四行全部编号,且每区各自从 1 起——三区编号策略独立、页号独立重置,这是 cat -n 完全做不到的。再实测 -d 自定义分隔符:page2.txt 里 %: 是字面行,nl -d "%:" 把它定义为分隔符:
$ nl -d "%:" /root/demo/nl/page2.txt
1 p1
p2
p3
p1 在初始 body 编号 1;第一个 %: 后进入 footer 区,p2、p3 默认 -f n 不编号——分隔符生效。对照组:-d "::" 定义 :: 为分隔符,page2.txt 里的 %: 只是普通内容行:
$ nl -d "::" /root/demo/nl/page2.txt
1 p1
2 %:
3 p2
4 %:
5 p3
五行全部连续编号,%: 作为普通行领走 2、4 号。同时验证了帮助原文「缺省第二字符补冒号」的约定:-d "%:" 是完整两字符,若只写 -d "%" 则分隔符实为 %:(% 补上默认的 :)。
💡 提示:日常纯正文加行号用不到三区,但处理「多段文档、每段重新编号」时它就是为这个设计的;不想让编号分区重置,用
-p(do not reset line numbers for each section)。
4.2 与 cat -n 对照:谁该用谁
同一份 poem.txt,cat -n 的输出:
$ cat -n /root/demo/nl/poem.txt
1 alpha line
2 bravo line
3
4 charlie line
5
6 delta line
7 123 numeric line
cat -n 给每一行编号(空行占 3、5 号),等价于 nl 的 cat -n 文件 ≈ nl -b a。把 nl 调成同样的编号策略与外观:
$ nl -b a -w 6 -s " " /root/demo/nl/poem.txt
1 alpha line
2 bravo line
3
4 charlie line
5
6 delta line
7 123 numeric line
两份输出几乎重合(差异仅分隔符:cat -n 用制表符,这里 -s " " 用两个空格)。
💡 技术拓展:「给行加号」的工具不止 cat -n 与 nl,常用对比如下——
方式 原理/特点 适用场景 cat -n所有输出行统一编号,空行占号,最快 快速临时看行号 nl三区逻辑页 + 四种 STYLE + 格式/宽度/分隔符全可调 排版级编号、跳空行、分段重编 grep -n只输出匹配行并带原文行号 检索定位,不改动文本 less -N/vim :set nu查看器内显示行号,不落盘 交互浏览时临时看号 关键差异:cat -n/nl 输出的是加了号的新文本(可进管道、可落盘),grep -n 报的是原始行号(用于定位),less/vim 的行号只显示不输出。本文实测采用 nl 做排版级编号——需要空行不占号、正则挑行、分区重编时它是唯一选项;纯临时看号用 cat -n 最省事。
五、注意事项与常见问题
Q1:nl 和 cat -n 到底差在哪?
两点:编号策略与分区机制。cat -n 恒等于「全行编号」;nl 默认 -b t 空行不占号(见 3.1/3.2 对照),还支持 -n/-w/-s 外观定制与 \: 三区逻辑页(见 4.1)。要 cat -n 的效果可用 nl -b a 复现(见 4.2)。
Q2:为什么 nl 输出里空行前有一段空格? 那是行号区:nl 对未编号的行也保留等宽的行号区(默认 6 列空格),保证内容列严格对齐(见 3.1/3.2 实测)。这是 nl 与「grep 加行号」类工具的显著区别。
Q3:-w 1 会把两位数行号截断吗?
不会。-w 设定行号区最小宽度,行号数字超宽时按实际位数输出,只影响对齐不影响数值。
Q4:-d "%" 为什么分隔符是 %: 而不是 %?
帮助原文约定:-d CC 需要两个字符,a missing second character implies ':'——只给一个字符时第二字符自动补为冒号(见 4.1 实测与对照)。
Q5:文件里恰好有 \: 字面内容的行被 nl 吃掉了?
是的,默认分隔符就是 \:,凡以 \:、\:\:、\:\:\: 开头的行都会被当作分区行处理(输出为空行)。冲突时用 -d 换一对不会出现的字符,或 `nl -d "::" 之类换符号(见 4.1 对照实测)。
Q6:nl 能从指定号数起步或改步长吗?
能。帮助原文列出的 -v N 设定每区起始号(默认 1)、-i N 设定步长(默认 1)、-l N 把连续 N 个空行计为一行(配合 -b a 使用)、-p 禁止分区重置。本文以默认起步与步长实测,其余参数按帮助原文同套语法使用。
六、参考链接
- GNU coreutils 官方手册(nl 章节):https://www.gnu.org/software/coreutils/manual/html_node/nl-invocation.html
- 在线文档:https://www.gnu.org/software/coreutils/nl
- 本机帮助:
nl --help、man nl、info '(coreutils) nl invocation'(Ubuntu 22.04 内置)