干过运维或者天天在终端里泡着的人,多半都有过这种体验:一条命令输出的结果太长,整行直接甩到屏幕外面,或者日志文件里某一行 JSON、一大段 SQL、一堆配置项,在终端里折得乱七八糟,想用 grep 定位关键字都费劲。有人会想到 sed、awk、fmt,但还有一个更纯粹的工具,名字就叫 fold,它专门解决“一行太长”这件事——按你指定的宽度,把长行切成一堆短行。
fold 是 GNU coreutils 里非常不起眼的一个小命令,日常存在感远不如 ls、grep、awk 这些明星工具,但真到用的时候,它比很多“重型武器”都顺手。它不排版、不美化、不智能重排,只做一件事:把输入的行按固定宽度硬折行。不管你是运维要切日志、开发要处理接口返回的长文本、数据分析要清理固定宽度数据,还是终端爱好者想写几个顺手的小脚本,fold 都能派上用场。
这篇文章我会从 fold 到底解决什么问题讲起,把字节、字符、列宽三种计数方式的差异拆清楚,再用五个实际场景演示它怎么用,最后总结我踩过的坑和一些扩展玩法。内容不复杂,看完基本就能直接上手。
1. 项目背景:fold 命令到底解决了什么问题
1.1 长行文本的日常困境
先设想一个很普通的场景:你手头有一个程序日志,每行是一个完整请求的 JSON,写了上千个字节。你想看看其中某个字段的值,于是习惯性执行 cat app.log,结果屏幕瞬间被拉出一条长到天边的线,终端自动换行把后面的字段甩得乱七八糟,更别提复制某个片段继续操作了。
这个问题的本质是:终端有显示宽度限制,但文本行本身没有换行符,两者一冲突,显示层面就只能“软换行”。软换行不影响文件内容,但影响人的阅读和处理。fold 做的事情,就是把这些长行按你给定的宽度,硬生生插入换行符,让物理行数和视觉行数一致。
这个“硬”字很关键。它不是按照单词边界去智能排版,而是在指定的列、字符或字节位置直接切断。因为行为简单,所以它可以在管道里非常高效地处理超大文件,不会像一些排版工具那样要构建完整的内存模型。
1.2 和 fmt、cut、awk 的定位差异
很多人听说 fold 之后,第一反应是“这不就是 fmt 吗”,或者“用 awk 也能做”。确实,它们有重叠,但定位完全不同。
fmt 是用来“重新排版段落”的,它会把多行合并、把短行补齐、调整单词间距和换行位置,让整段文字看起来像一篇排好版的文档。这适合处理邮件正文、说明文件、注释文本。fold 则不管语义,它不合并行,也不调整间距,只是简单地把长行切断,适合处理日志、数据流、固定宽度记录这些结构化的内容。
cut 是按分隔符或字符位置“截取”字段,输出的是原来行的片段,不会改变行的数量。fold 则是把一行变多行,保留所有内容,只是改变换行位置,两者方向上就不一样。
awk 理论上可以做任何文本处理,但想按列宽折叠,你得用 substr 加循环,还得自己管理输出缓冲区,写起来繁琐且容易出错。fold 一行命令搞定,参数明确、行为可预期,在流水线脚本里更省心。
1.3 典型使用场景
根据我的实际使用经验,fold 最常见的用武之地大概有四类:
- 日志和长文本预处理:把超长的日志行折成适合阅读的宽度,再配合
grep、sed做过滤。 - 固定宽度数据处理:老系统导出的定长记录、银行对账单、设备上报的数据流,需要用指定字节数或字符数切开。
- 终端输出控制:在脚本里限制某段输出的最大宽度,避免打乱终端布局。
- 与十六进制查看、二进制分析工具配合:比如把数据流按每 16 字节一行切开,方便后续处理。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心参数解析:从字节、字符到列宽
2.1 语法和基本参数
fold 的语法非常简单,基本形式是:
bash复制fold [选项]... [文件]...
如果不指定文件,就从标准输入读数据。常用选项就四个:
| 参数 | 作用 |
|---|---|
-w N |
按 N 列显示宽度折叠,默认 80 |
-b |
按 N 字节折叠,而不是按列 |
-c |
按 N 字符折叠,而不是按列 |
-s |
在空格处断行,避免切断单词 |
另外还有 -- 用于结束选项解析,以及 --version、--help 这类标准选项。
默认行为是 fold -w 80,意思是按 80 列显示宽度折行,遇到超过宽度的行就切断。对纯 ASCII 的英文文本来说,80 列是个很经典的老终端宽度,现在屏幕宽了可以按需调整。下面这个例子就是把一段话按 20 列宽度硬切:
bash复制echo "This is a very long line that will be folded" | fold -w 20
输出大概是:
code复制This is a very lon
g line that will b
e folded
可以看到默认情况下它不分青红皂白,在 20 列处直接切断,单词 long 被劈成了 lon 和 g,be 被劈成了 b 和 e。这其实是很多人第一次用 fold 觉得它“笨”的原因,但这也是它“简单可靠”的体现。
2.2 三种计数单位的本质差异
-b、-c、-w 之间的关系,是使用 fold 最容易绕晕的地方。我拿具体例子说明。
先看 -b(按字节)。字节是最底层的单位,一个 ASCII 字符占 1 字节,一个中文 UTF-8 字符占 3 字节。fold -b -w 4 表示每 4 个字节折一行,这个对处理二进制数据或者固定字节长度的记录非常有用,但对多字节文本很危险,因为很可能在字符中间切断,导致乱码。
-c(按字符)则是按字符数来算,一个汉字算一个字符,一个英文也算一个字符。对于想“每行几个字符”的需求,它比 -b 直观很多,也不会切断多字节字符。
-w(按列宽)最特殊,它按“显示宽度”来算。在终端里,英文字母宽度是 1,大多数 CJK 字符宽度是 2,所以 fold -w 10 在中文环境下大约能放下 5 个汉字。这个模式最贴近终端显示效果,但底层依赖系统的字符宽度判断,不同 locale 下表现会有细微差异。
我整理了一个对比表格,直观展示差异。假设输入是 abc你好,一共 3 个 ASCII 字符加 2 个汉字:
bash复制echo "abc你好" | fold -b -w 4
echo "abc你好" | fold -c -w 4
echo "abc你好" | fold -w 4
这三种命令的输出会不太一样。-b -w 4 按字节数切,第 4 到第 6 字节正好是一个汉字的 UTF-8 编码后半段,所以可能产生乱码;-c -w 4 按字符数切,每行 4 个字符,汉字不会被切断;-w 4 按显示宽度切,abc 占 3 列,再凑 1 列不足以放下一个汉字,断行位置就会显得“别扭”。
注意:如果你在多字节环境下使用
fold,请先确认系统 locale。执行locale命令,如果输出里的LC_CTYPE是C或POSIX,一些字符宽度判断可能不准。建议在C.UTF-8或en_US.UTF-8这类环境使用-w相关功能。
2.3 -s 与 -w 的组合使用
如果处理的是普通英文文本,默认的硬切断体验很差,这时候 -s 参数就派上用场了。-s 不是单独设定宽度,而是告诉 fold:在折行时优先寻找空格,在空格处断开,不要硬生生切断单词。
bash复制echo "This is a very long line that will be folded" | fold -s -w 20
输出就好看很多:
code复制This is a very
long line that will
be folded
注意 -s 是“尽力而为”。如果一个单词本身超过了指定的宽度,比如一个超长的 URL 或者一个很长的连续字符,fold -s 找不到合适的空格,最后还是会在宽度边界硬切。你可以把 -s 理解成一个偏好选项:能优雅就优雅,不能优雅就保底。
3. 实操演示:五个常见场景的折叠实现
3.1 场景一:把超长日志折成可读行
运维排查日志的时候,最烦的就是一行日志包含了一个完整的堆栈或 JSON,直接在终端里看会折得乱七八糟。我的惯用做法是先折行、再过滤:
bash复制cat app.log | fold -s -w 120 | grep -A 20 "ERROR"
-w 120 是配合现代终端宽度,-s 保证尽量在单词或空格处断行,这样 grep -A 20 输出的上下文也不会被拉成天线。如果日志里有 JSON 这种没有太多空格的文本,-s 的效果会打折扣,但还是比默认硬切强。
这里有个小技巧:你可以把这条命令定义成一个 shell 函数,写进 .bashrc:
bash复制logtail() {
tail -f "$1" | fold -s -w 120
}
以后排查日志直接 logtail app.log,所有输出都自动折行,不用每次手打一长串。
3.2 场景二:按固定字节切开二进制或定长数据
很多设备上报的数据、老系统导出的银行账单、特定协议抓包后的载荷,都是“定长记录”格式。比如每条记录固定占 64 字节,你想把它按 16 字节一段切开做进一步分析,fold -b 非常合适:
bash复制some_program | fold -b -w 16
-b 保证按字节切,不管你后面接的是 xxd、od 还是自己写的解析脚本,每个输出行的字节数都能严格对上。
我实际做过一个设备采集程序的数据预处理:设备每 5 秒上报一串十六进制流,程序解析前需要按字段长度切分。最初我用 dd + head -c 重复调用,效率很低,换成 fold -b -w 字段长度 之后,一条管道就解决了切分问题,再配合 while read 逐行处理,代码简洁得多。
3.3 场景三:处理多字节中文不乱码
中文用户最容易遇到的问题,就是 fold 把汉字切成了乱码。我见过不少人在脚本里直接写 fold -b -w 10,结果输出一堆 �,就开始抱怨命令不行。其实问题不在 fold,而是选了错误的计数单位。
处理中文文本,优先用 -c 或 -w,不要用 -b。区别在于:-c 按字符数切,直观不易错;-w 按显示宽度切,更适合终端阅读。
bash复制echo "你好世界,这是一个测试" | fold -c -w 5
输出:
code复制你好世界,
这是一个测
试
注意因为 -c 按字符数算,最后一行只有一个字。如果改用 -w,因为中文显示宽度算 2 列,-w 10 大概每行能放 5 个汉字,效果和上面类似。不同系统上的字符宽度判断细节可能有差异,所以建议在自己的环境里先用一小段样本测试,确认输出符合预期再跑全量数据。
如果你的文本里混着中英文,想按“显示效果”而不是字符数折行,用 -w 更科学,因为它能把两个英文算一列宽、一个汉字算两列宽,出来的视觉效果更整齐。
3.4 场景四:固定列宽表格的预处理
有时候数据源是一行接一行的长字符串,比如从数据库导出的一串固定宽度字段,你想在终端里临时按列查看,可以先用 fold 切好行,再用 paste 拼成列:
bash复制echo "12345678901234567890abcdefghijklmnopqrstuvwxyz" | fold -w 10
输出:
code复制1234567890
abcdefghij
klmnopqrst
uvwxyz
再用 paste - - - - 把四行并成一行四列:
bash复制echo "12345678901234567890abcdefghijklmnopqrstuvwxyz" | fold -w 10 | paste - - - -
输出变成:
code复制1234567890 abcdefghij klmnopqrst uvwxyz
这种组合方式在临时查看固定宽度数据时非常实用,不需要写脚本,一条管道就出了结果。配合 column -t 还能做成对齐的伪表格,后面扩展玩法里我再细说。
3.5 场景五:在脚本里优雅地控制输出宽度
写脚本的时候,经常需要把某段输出限制在一定宽度,避免破坏终端的布局。比如一个部署脚本要打印配置摘要,最长的一行可能有上百个字符,你可以用函数封装:
bash复制wrap() {
fold -s -w "${1:-80}"
}
然后任何地方需要控制宽度,就:
bash复制printf "%s\n" "$long_string" | wrap 100
这个 wrap 函数不占用额外参数,输入从标准输入进来,输出也是标准输出,接入管道很自然。如果希望在折行后保留缩进,可以先用 sed 给每行加前缀,再 fold,注意 fold 按整行宽度计算,所以加了前缀后每行能容纳的正文其实变少了,你需要在宽度参数里把前缀长度算进去。
4. 常见问题与避坑指南
4.1 中文乱码问题
前面已经提到,乱码的根本原因是 -b 按字节切断了多字节字符。复现很简单:
bash复制printf '中文测试\n' | fold -b -w 3
输出几乎一定是乱码,因为一个汉字在 UTF-8 里占 3 字节,-b -w 3 恰好把它切成三段。
解决方案也很明确:
- 只想按字符数切:用
-c。 - 想按显示宽度切:用
-w,并确认 LC_CTYPE 是 UTF-8。 - 如果必须按字节切且不能乱码,就得事先分好消息边界,或者改用其他工具(比如
dd+ 编码转换)。
我在生产环境踩过一回:拿别人写的脚本处理订单文本,对方图省事用了 fold -b,结果把客户姓名切成了乱码记录,后面程序解析全错。后来我把 -b 改成 -w,异常立刻消失。多字节环境下,参数选错会埋雷。
4.2 单词被硬生生劈开
默认 fold 就是硬切,所以英文文本很容易出现半个单词。这不是 bug,是设计使然。想避免,加 -s。
但要注意,-s 只认空格作为断点,不会识别其他分隔符,比如连字符、斜杠、下划线。遇到 URL 这种长字符串,fold -s -w 30 可能还是会把 URL 中心部分切断,因为整段没有空格。如果你想对 URL 或路径这类内容做智能断行,fold 帮不了太多,要么先手动替换分隔符为空格,要么改用 fmt 或者专门的文本工具。
4.3 fold 与 fmt 被混淆
网上经常看到有人问“fold 为什么不能把多行按段落排版”,其实这是把 fold 和 fmt 混为一谈了。fold 只做“把长行强行分成多行”,它不会去合并短行、不会去调整对齐、不会重新组织段落。fmt 才是那个做段落重排的工具。
举个例子,一个文件里既有超过 80 列的长行,也有不足 10 列的短行,fold 只会处理长行,短行保持原样;fmt 则会把它们合并重排成连续的文字块。
所以选型建议是:
- 保留原始结构,只想把长行切断:用
fold。 - 想把文本段落重新排版,让整体更接近自然文档:用
fmt。 - 既想按宽度切又想保持某种语义:先用
fold,再用sed或awk做后处理。
4.4 在管道中的隐藏问题
fold 是逐行按需输出的流式命令,不缓存全文,因此处理超大文件时内存占用很低,这点很优秀。但在管道里有两个隐藏问题值得注意:
第一,如果管道后面的命令提前退出,比如 some_big_file | fold -w 100 | head -n 10,fold 往管道写数据时可能收到 SIGPIPE 信号,默认行为是终止。这在大多数场景下没问题,但如果你的脚本用 set -o pipefail,管道退出码可能会变成非零。你不希望脚本因此误报失败,可以在管道尾部加 || true,或者显式处理退出码。
第二,fold 按字节读入行时,会保留原有换行符,所以行尾的字符不会变。但如果你在一个文本流里混合了 \r\n(Windows 换行),fold 会把这个 \r 也当成普通字符计入宽度,导致折行位置比预期少一列。处理 Windows 格式文本时,建议先用 sed 's/\r$//' 或者 dos2unix 清理,再交给 fold。
4.5 字节、字符、列宽差异速查表
| 需求 | 推荐参数 | 说明 |
|---|---|---|
| 纯 ASCII 英文按宽度折叠 | -w 80 |
最常规用法 |
| 不希望切断英文单词 | -s -w 80 |
在空格处断行 |
| 按字节切分,适合二进制 | -b -w N |
注意多字节乱码 |
| 按字符数切分 | -c -w N |
中英文都按一个字符算 |
| 按显示宽度切分,适合终端 | -w N |
依赖 locale 字符宽度 |
5. 扩展玩法与我的使用心得
5.1 用 fold 做简单文本对齐
fold 配上 column 能玩出不少花样。比如你有一个超长的数据行,想把它变成多行对齐的伪表格,可以先 fold 再 column -t:
bash复制echo "Name:Alice Age:30 City:Shanghai" | tr ' ' '\n' | fold -s -w 20 | column -t
这里 fold -s -w 20 把分隔后的内容按宽度折行,column -t 再按空格对齐。虽然组合有点绕,但在没有安装其他工具的情况下,能快速做出可读性不错的输出。
5.2 我常用的几个手写函数
我习惯把 fold 的常见用法封装成函数,放到 .bashrc 里,提高日常效率:
bash复制# 按显示宽度折行,默认 100,保留单词
wrap() { fold -s -w "${1:-100}"; }
# 按字节数折行,处理二进制或定长记录
wrap_bytes() { fold -b -w "$1"; }
# 按字符数折行,处理多字节文本
wrap_chars() { fold -c -w "$1"; }
三个函数对应三种典型需求,参数含义一目了然。以后凡是遇到长行,直接 echo "$var" | wrap 80,不用再每次查 man。
5.3 性能心得
fold 虽然小巧,性能也很关键。我做过一个不严谨的测试:一个约 1GB 的纯文本文件,fold -w 120 在普通服务器上跑完大概只要十几秒,内存占用可以忽略不计,因为它逐行处理、不缓存全文。而 fold -s 因为有查找空格的逻辑,耗时略高,但仍然比 fmt 快得多。
如果你要在脚本里处理千万行级的文本,fold 是可靠的选择。但要注意,如果输入里有极长的行(比如几十 MB 的单行),fold 依然需要把这行读入内存,所以内存占用会随单行长度增加。遇到这种极端情况,可以考虑先 split 拆文件,或者用流式方法逐步切割。
5.4 最后一点经验
用 fold 这么久,我最大的感触是:它不适合被当成一个“万能排版工具”,它更适合当成一个“低层文本切分原语”。你需要先明确需求是“换行”还是“排版”,前者用 fold,后者用 fmt 或者其他工具。参数选择上,-b、-c、-w 三种模式针对的是不同需求,千万别因为看着差不多就混着用。
另外,多字节文本一定要提前测试。我见过太过案例,因为一句 fold -b 导致中文乱码,整个数据处理链路崩掉。所以写进正式脚本前,先拿几个不同语言、不同长度的样本验证输出,确认没问题再跑全量,这是最稳妥的做法。
如果你正在被长行文本困扰,下次可以试试 fold。它很小,但确实能解决很多看着不大、实际很烦的痛点。
