在 Linux 系统中,fmt 命令是一款专门用于整理文本段落的小工具。它能够读取文本内容、识别段落边界、合并短行、折行长行,同时保留空行作为段落分隔符——无需额外参数,就能将杂乱无序的文本整理成规整的段落式排布,极大提升文档的可读性。
不过,实际使用时往往没那么简单。不少用户第一次接触 fmt 用法时就碰了壁:文件格式化后反而全被挤成了一行;代码注释中启用 -p 参数,前缀却不翼而飞;通过管道输入数据时,输出结果错位严重、难以辨认。这些都是 fmt 命令常见问题,下面逐一说明背后的原因与解决办法。
直接格式化文件时为什么输出乱成一行?
这并非 bug,而是 fmt 默认行为:它把每个逻辑段落——即用空行分隔的文本块——先压缩成单行再重新排列。如果你看到“所有内容挤在一行”,说明原始文本里根本没有空行,fmt 段落识别自然将整篇当作同一段处理。
那么,如何排查?使用 cat -A file.txt 查看源文件中是否存在 ^M 或空白行。如果确实没有空行,又想强制按物理行切分,可以加 -s 选项(只负责拆分不重新组合),再配合 -w 指定输出宽度,例如 fmt -s -w 60 file.txt。另外需注意,如果原始文本中混有制表符或混合空格缩进,fmt 段落边界可能被误判,建议先用 sed 's/^[[:space:]]*//' 清理首部空白再尝试。
处理代码注释时 -p 选项总丢前缀?
-p 参数要求前缀严格匹配行首才算数。格式化后,fmt 自动添加前缀到每一行——前提是该行以该前缀开头且前面没有空格。很多人在这里栽跟头。
常见失败场景有三个。第一,注释写成 " # long comment",前面多出空格,fmt -p 前缀丢失自然不生效。解决办法是先执行 sed 's/^ * #/#/' 去除空格。第二,前缀本身带空格,比如 "# ",必须写成 -p '# ',少写一个空格或引号未闭合都会报 invalid option。第三,多行注释中混用了 # 和 //,fmt 一次只处理一种前缀,需要分两次调用才能覆盖全部情况。
-c 和 -t 都说保留缩进,区别在哪?
简单来说,-c(crown-margin)让段落第一行顶格,后续行缩进;-t(tagged-paragraph)正好相反:第一行缩进,后续行顶格。具体使用哪个,取决于你文本原有的缩进结构。
如果段落是首行缩进两格、其余顶格,用 -t。如果段落是首行顶格、第二行起缩进,选 -c。两个都不生效?很可能是原始缩进使用了制表符,而 fmt 缩进处理默认按空格计算宽度。可以先加 -u 参数将所有空白统一,再试一次。
管道输入时格式化结果突然变窄或错位
从管道传入的数据没有文件元信息,fmt 管道输入容易误判段落边界。最典型的场景是:一条长命令的输出被切成碎片,或者换行位置看起来特别诡异。
要避免这个问题,首先得确保输入是纯文本段落,没有混入 ANSI 转义序列——例如 ls --color 的输出就不能直接喂给 fmt,先过一遍 cat -v 检查。如果用 echo 测试,记得加引号:echo "long line" | fmt -w 40。不加引号的话,shell 会先按空格把内容切碎再传给 fmt 格式化管道数据,结果自然不对。如果想保留原始换行逻辑,可以试试 -d 选项,但注意该选项并非所有发行版都支持——Ubuntu 22.04 以上有,CentOS 7 默认没有。
说到底,fmt 段落识别机制完全依赖空行和行首空白,它不解析语义。你给它一篇 Markdown,它也只当普通文本处理。真正要安全格式化,得先剥离标记、统一缩进,再喂给 fmt。这活看着简单,细节上坑不少。
