1. 先从一次“惨痛”的文本处理说起
我大概在刚接触 Linux 运维那会儿,遇到过这么一件事:线上有个服务日志,每天凌晨会往文件里追加将近两万行访问记录,格式是约定的 IP地址|访问时间|请求路径|状态码。当时要做一个快速统计,只需要把每行里的 IP 和状态码抽出来,老板催得急,我第一反应是写个 while read 循环再配 awk -F'|' '{print $1, $4}'。awk 确实能搞定,但那次脚本跑到一半,终端卡顿得厉害,处理完足足花了两分钟。
后来一位同事看了一眼,说了句:“你直接用 cut 不就行了?”我改成 cut -d'|' -f1,4,五秒钟不到就出结果了。从那天起,我才认真去研究 cut 这个命令。说真的,在 Linux 常用命令里,cut 就像工具箱里那把特别顺手的美工刀,功能范围不算广,但遇到需要按列切割文本的场景,在效率和简洁性上能吊打很多你原以为“更高级”的工具。
这篇文章不打算把 cut 的所有细节罗列成一本“手册”,而是按我实际工作中的使用路径来讲:先理解它的设计思路,再拆解三个工作模式和范围语法,然后放进几个真实场景里跑一遍,最后把踩过的坑和排查思路整理成速查表。无论你是刚开始学 Linux 的新人,还是用了好几年但仍然习惯性写 awk 的老手,这篇文章都能让你对 cut 有一个更系统的认识。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 理解 cut 的核心设计:它到底在“切”什么
2.1 cut 与 awk 的定位差异:为什么有时用 cut 更合适
很多教程在讲 cut 时喜欢直接列参数,但我建议大家先想清楚一个问题:awk 也能按分隔符取列,为什么还要用 cut?我的理解是,awk 是一个完整的“文本处理语言”,它有变量、判断、循环、内置函数,它是“武器库”;而 cut 是一个纯粹的“列提取器”,它不做运算,不搞状态管理,不读入内存再去逐行解析,它就是拿一个“行”进来,按照你指定的切法,原样输出需要的部分。
也正因为 kernel 底层实现得简单,cut 在处理超大型文件时优势非常明显。我自己做过一次粗略对比:从一个 500MB 左右的日志文件里提取某两列,awk -F',' '{print $1,$3}' 大概耗时 7.8 秒,而 cut -d',' -f1,3 耗时 3.2 秒。如果你需要在一台只分配了 2GB 内存的小机器上处理几个 GB 的文本,这种差距会直接决定任务能不能在当前环境下跑完。
所以我现在的选择标准很简单:如果只是“纯粹地按分隔符把第几列取出来”,优先 cut;如果还需要“对已取出的列做正则匹配、做数值比较、做统计求和”,那才上 awk。cut 也有自己的短板:它只能按分隔符、字符位置或字节位置来切,不能按“关键字”切。这一点我们到后面第 5 节专门讲,因为它恰恰是很多人踩坑的地方。
2.2 三个核心参数:-f、-c、-b 的本质区别
cut 的工作模式围绕三个参数展开:
-f(field):以字段为单位切割,核心前提是先通过-d指定分隔符。-c(character):以“字符”为单位切割,适合处理按固定宽度排列的文本。-b(byte):以“字节”为单位切割,能精准控制字节偏移,常用于处理二进制导出文件或严格按字节对齐的数据。
这三个参数本质上是三种不同的“标尺”。很多初学者会把 -c 和 -b 搞混,觉得“字符不就是字节吗”?在纯英文环境里确实一样,但一旦遇到中文,区别就大了。中文字符在 UTF-8 编码下通常占用 3 个字节,在 GBK 编码下占用 2 个字节。你如果知道目标文件是 UTF-8 编码,但数据前几列恰好是“一个汉字 + 几个字母”的混排,用 -c 1 可以正确取出那个汉子,用 -b 1 可能只会切出半个字符的输出,轻则乱码,重则导致后续程序解析崩溃。
我整理了一张对照表,方便大家快速理解:
| 参数 | 切割单位 | 典型场景 | 能否处理多字节字符 |
|---|---|---|---|
| -f | 字段(两两分隔符之间) | 处理 CSV、/etc/passwd、日志 | 能,与分隔符无关 |
| -c | 字符 | 固定宽度的文本排列 | 能,按语言编码取整字符 |
| -b | 字节 | 二进制数据、纯 ASCII 定长记录 | 不建议用于中文 |
这个表也是我在面试 Linux 候选人时最常问的一组概念,能把 -b 与 -c 的区别讲清楚,说明对底层字符编码是有概念的,而不只是背命令。
3. 逐层拆解 cut 的实操用法
3.1 字符列提取:理解固定宽度文本的 C 语言思维
先说 -c,因为它最直观。设想你有一个 students.txt 文件,内容是:
code复制学号 姓名 数学 语文
1001 张三 89 92
1002 李四 75 88
1003 王五 93 79
这个文件里的每一列,都是严格按照固定字符宽度对齐的。学号从第 1 列到第 4 列,姓名从第 6 列到第 8 列,数学从第 11 列到第 12 列,语文从第 15 列到第 16 列(这里为了演示方便,我做了简化,实际中你遇到的对齐文件会有更复杂的空格规则,所以建议先跑 cat -A 看看到底是空格还是制表符)。如果只想要“姓名 + 数学成绩”,可以这么写:
bash复制cut -c 6-8,11-12 students.txt
输出结果就是:
code复制张三 89
李四 75
王五 93
这里 6-8 是一个“范围区间”,cut 接受的范围语法很丰富,它不只支持 N-M,还支持下面这些写法:
N:只提取第 N 个字符(或字段、字节)。N-:从第 N 个一直提取到行尾。-M:从行首提取到第 M 个。N-M:从第 N 个提取到第 M 个,闭区间。
你可以把这些区间用逗号组合起来,比如 -c 1-4,6-8,11-12,甚至可以和 -b、-f 组合使用。有一点需要特别注意:无论你给的区间顺序是 11-12,6-8,cut 都保证输出时按照原始文本的从左到右顺序排列,这正是“提取”与“重新排序”的本质区别。如果你需要重排列顺序,那是 awk '{print $3, $1}' 的活。
为什么这种能力很有用?因为很多老旧的系统导出的文本报表,依旧是固定宽度格式。配置文件里的某些段落,也常常有这种“每个字段占多少列”的约定。掌握 -c 之后,你甚至能做到完全不用分隔符,直接“按位置”从一行文本里抽数据,这对处理某些没有统一分隔符的脏数据很有帮助。
3.2 字节列提取:不得不说的编码边界问题
-b 参数是我使用频率较低、但一旦用上就非常依赖的参数。它适合处理“每一行都是严格按字节对齐”的文件,常见于从某些维护系统导出的 DBF 数据、固定长度的报文文件、某些老式主机对接时产生的 EBCDIC 转码文本。在这些场景里,一个字段占了多少字节是协议规定好的,你必须按字节去切,而不是按字符。
举个例子,假设有一个 data.raw 文件,每行内容如下(用空格隔开的不是真实字段边界,只是为了让我方便展示):
code复制001ABCNikeAir
002DEFAdidas
其中前 3 个字节是流水号,接下来 3 个字节是区域代码,后面全是商品名称。你可以用:
bash复制cut -b 1-3 data.raw
cut -b 4-6 data.raw
cut -b 7- data.raw
一次把流水号、区域代码、商品名称分别提取出来。
正是在这种用法下,多字节编码的坑最容易暴露。如果你在 UTF-8 环境下对一个包含中文的每个汉字 3 字节执行 cut -b 4-5,很可能会把某个汉字的字节序列切断,输出一个“�”之类乱码字符。这种情况不是 bug,而是cut 是按字节切,没有能力去“理解”字符边界。要避开这个坑,可以这样想:
- 确定数据中不存在多字节字符,直接用
-b。 - 数据中可能存在中文,且你需要按“显示位置”切,用
-c。 - 数据中既有定长字节需求,又有中文内容,先在文件层面处理好编码(统一转为 UTF-8),再计算每个中文字符占 3 个字节的偏移,谨慎使用
-b,或者考虑用其他工具配合。
3.3 字段列提取:真正核心的日常武器
-f 才是绝大多数场景下你真正会用到的主力参数。它配合 -d 指定分隔符,把每行拆成若干字段。“字段”这个词你可以理解成 Excel 里的“单元格”,两个分隔符之间的所有内容就算一个字段。
最经典的例子是解析 passwd 文件:
bash复制cut -d: -f1 /etc/passwd
这会把系统里所有用户名列出来,因为 /etc/passwd 的每行格式是固定的:
code复制root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
第1列 是用户名,第3列 是 UID,第6列 是家目录。如果你要排查某个用户的家目录,可以这样:
bash复制cut -d: -f1,6 /etc/passwd
输出类似:
code复制root:/root
daemon:/usr/sbin
注意这里输出时,cut 会保留原始行的字段顺序,同时用分隔符重新连接。如果你给的提取字段不是连续的,它不会自动帮你合并,而是会用同一个分隔符把它们接起来。所以 -f1,6 会得到 root:/root,和原始行里的字段顺序一致。这是 cut 的优势之一:提取结果依然容易被人和后续工具理解。
-f 也支持“排除字段”的补集逻辑,用 --complement 可以提取“除了某些字段以外的所有内容”。比如:
bash复制cut -d: -f1 --complement /etc/passwd
在没有指定 -f 范围时,默认是提取第 1 个字段到第 10 个字段,这里配合 --complement 会输出除了第一个字段外的所有字段。这个参数用得好可以省不少事,比如你想去掉某行数据中的“序号”列,同时保留剩下所有字段。
4. 实战复盘:从用户名提取到日志切割
4.1 场景一:系统用户清单快速格式化
假设你接到一个任务:运维同事需要一份“普通用户”清单,所谓普通用户就是在 /etc/passwd 中 UID 大于等于 1000 的用户。很多初级工程师第一反应是写一个 awk 脚本:
bash复制awk -F: '$3 >= 1000 {print $1}' /etc/passwd
这个写法没问题,结果也是对的。但我更想展示的是“组合拳”的思维:你可以先用 cut 把关键列提取出来,做一次粗加工,再用其他工具做细加工。比如:
bash复制cut -d: -f1,3 /etc/passwd | awk -F: '$2 >= 1000 {print $1}'
第一次用 cut 把本不需要的 x、GID、家目录、shell 这些字段都去掉,数据量变小,后续管道再处理时压力也小。一次大数据量的文件,这种“先瘦身再处理”的思路能让整个脚本的执行时间显著下降。
从整体流程和易用性来看,把 cut 放在管道的最前端,用它做一次“列级的预过滤”,是一个值得养成的习惯。因为管道里的数据越早变小,后面每个环节的开销都会整体降低。
4.2 场景二:日志关键字段提取与多维组合
日志分析是 cut 最频繁使用的战场。我日常常见的日志格式是类似这样的 CSV 风格:
code复制2025-06-18 10:23:45,INFO,192.168.1.10,user_login,success,elapsed=120ms
2025-06-18 10:23:50,ERROR,192.168.1.20,user_login,failed,elapsed=35ms
2025-06-18 10:24:01,WARN,192.168.1.10,file_upload,timeout,elapsed=2000ms
假设日志文件是 app.log,我想统计一下“今天所有耗时超过 1 秒的操作” 的 IP 和操作类型,我可以分两步走:
第一步,用 cut 提取关键列:
bash复制cut -d',' -f3,4,6 app.log
输出:
code复制192.168.1.10,user_login,elapsed=120ms
192.168.1.20,user_login,elapsed=35ms
192.168.1.10,file_upload,elapsed=2000ms
第二步,如果进一步需要过滤掉 elapsed<=1000ms 的行,再用 grep 或 awk 做二次加工:
bash复制cut -d',' -f3,4,6 app.log | grep -E 'elapsed=[1-9][0-9]{3,}ms'
这样能快速拿到目标行了。这里有一个细节:提取列时用 -f3,4,6,输出会自动把字段用分隔符重新连起来,这里分隔符是逗号,所以输出仍然是逗号分隔,这是很好用的行为,不需要额外 -j 或者 paste 去拼接。
4.3 场景三:多分隔符文本的预处理
实际业务中真有那么一批日志,字段分隔符不统一。比如:
code复制主机名|IP地址 状态码 耗时(ms)
web01|192.168.1.10 200 120
web02|192.168.1.11 500 35
既有 | 又有空格,直接按单个分隔符切会乱。这时可以先把 | 替换成空格,再使用 -d' ' 去切,或者把某个分隔符转化为目标分隔符,再交给 cut。一个常见做法是:
bash复制tr '|' ' ' < access.log | cut -d' ' -f1,3
第一段用 tr 把所有竖线替换成空格,让每行在各个字段之间刚好有一个空格,然后 cut -d' ' -f1,3 就能正确提取“主机名”和“状态码”。这种“先清洗再切割”的思路,在处理不规范文本时几乎是必备手法。
还有一个频繁踩坑点:文件里确实是“空格”,但可能是多个连续空格,或者 Tab 与空格混用。如果你直接写 -d' ',cut 会把连续空格当成“多个分隔符”,从而产生空字段,结果完全对不上。我自己的排查习惯是,先跑 cat -A 把行尾和 Tab、空格全部显形,看清楚再定分隔策略。
4.4 参数选择速查表
这里我整理一个我自己会贴在终端旁边的速查表:
| 需求 | 推荐写法 | 备注 |
|---|---|---|
| 提取第 2 列 | cut -d',' -f2 |
分隔符自己换 |
| 提取第 1 和第 3 列 | cut -d',' -f1,3 |
顺序保持原样 |
| 提取前 10 个字符 | cut -c1-10 |
按字符 |
| 提取第 5 个字符之后的所有 | cut -c5- |
从 5 到尾 |
| 提取第 3 个字段到行尾 | cut -d':' -f3- |
区间右开 |
| 排除第 2 列,其余全取 | cut -d',' -f2 --complement |
类似反选 |
| 只取每行前 4 个字节 | cut -b1-4 |
适合纯 ASCII |
5. 常见问题与排查技巧实录
5.1 错误一:文件用的是 Tab 制表符,却用空格分隔
这是我见过最多的错误。即使文本看起来“每列之间有几个空格”,实际上可能是 Tab。比如你从某些数据库导出的数据就是 Tab 分割。此时如果执行:
bash复制cut -d' ' -f1
结果大概率一整行都被当成第 1 个字段,或者拆出大量空字段。正确手段是先 cat -A file 看分隔符:Tab 会显示为 ^I。确定之后有两种修法:
- 如果文件本身是 Tab 分割,直接
cut -f1,3即可,因为cut在没有显式-d时,默认分隔符就是 Tab。 - 如果你想把文件转为空格分割再处理,用
expand命令把 Tab 转成空格,再用-d' '去切。
5.2 错误二:多字节字符导致 -c 和 -b 结果错乱
这个我只在中文环境下处理的场景里比较容易遇到。文件是 UTF-8 编码,我拿 cut -b 1-5 想取前 5 个字节,出来的却是乱码。原因是第 5 字节恰好落在某个汉字的中间。此时有两个方向:
- 如果不区分字符边界,不想看到乱码,用
-c 1-5换成按字符取。 - 如果你的需求必须按字节取,且输出要送到下游程序,那就得保证切割点不要落在多字节字符内部,通常需要先明确文件中每个字段占多少字节,再做“字节偏移对齐”的设计。
5.3 错误三:想按关键字提取列,但 cut 做不到
很多新手会写出类似 cut -d':' -f'user' 这种自以为能按“字段名”提取的代码。这里必须强调:cut 不支持按名字取字段,它只认“位置”。就像你在 Excel 里要说“B 列”,不能说“年龄那一列”一样。它没有列头感知能力,没有状态记忆。
因此,如果你需要“找出用户名为 zhangsan 的那一行的 UID”,第一反应不应该是 cut,而应该用 grep '^zhangsan:' /etc/passwd | cut -d: -f3。先拿 grep 定位行,再用 cut 取列。这就是 cut 在管道里的经典协作姿势:它不负责“判断”什么,只负责“提取”。
5.4 错误四:区间越界没有报错,但结果完全为空
cut 的另一个特性是,如果给了一个超出行长度的区间,它不会报错,只会输出实际存在的部分。比如某行只有 10 个字符,你 cut -c 20-30,输出就是空行。这个特性在批量处理时尤其容易埋雷:程序不会中断,但到你最后统计数据时才发现少了内容。排查手段是对输出结果做一次行数对比:
bash复制wc -l 原始文件.txt
cut -c 20-30 原始文件.txt | wc -l
如果两者行数差异较大,说明你提取的空行太多了,需要检查区间定义是否符合数据实际宽度。
5.5 特殊技巧:cut 处理前后空格等隐形字符的技巧
有时文件里的字段有首尾空格,比如 " zhangsan , 23 " 这种,直接 cut -d',' -f1 出来的结果会带着空格。如果你还要拿这个值去做精确匹配,极易失败。我的习惯是在 cut 之后接 sed 's/^[[:space:]]*//; s/[[:space:]]*$//' 或者 awk '{gsub(/^[[:space:]]+|[[:space:]]+$/, ""); print}',先做一次清理。注意,cut 本身不负责清理空格,awk/gsub 才是这个环节的主角。
6. 关于 cut 面试考察与学习路径的建议
6.1 面试中常见的 cut 高频考点
作为一个经常参与面试的人,我发现 cut 在 Linux 运维和测试岗位的面试题里出现频率很高,而且考察点会比想象中细。常见的有:
- “
cut和awk都能按分隔符取列,什么场景切优先用 cut?”答案就是第 2.1 节提到的性能与简洁性。 - “
cut -c和cut -b有什么区别?中文环境下会有什么问题?”这是考察底层编码理解的经典问题。 - “一个文件是逗号分隔,但某列内部也有逗号,怎么用 cut 处理?”这种情况 cut 很难直接回答,因为 cut 不支持“引号感知”。如果 CSV 字段内有逗号,用 cut 会把你引号内的逗号也切分成字段,这也是 awk 配合 FPAT 或类似库更适合 CSV 解析的原因。这一点要能讲清楚,才说明你真的理解 cut 的边界。
- “如何提取一个文件的第 2 到倒数第 2 个字段?”cut 没有“倒数”的概念,你只知道总字段数时才能反推范围,或者用
rev反转后处理。这些都是可以深入思考的点。
6.2 我给新手的学习路径建议
如果你刚入门 Linux 命令行,我不建议你一口气把 awk 的所有高级功能学完。我的建议是:
- 先熟练掌握
cut的-f、-d,配合cat、head、grep、wc完成大部分简单的日志列提取任务。 - 再学习
sort、uniq、tr,学会用管道组合,形成“文本处理的流水线思维”。 - 等处理过一批真实日志,知道哪些问题用单纯 cut 解决不了之后,再系统学习
awk和sed。
这个路径的好处是每一步都“有得用”,不会产生“学了全套命令但不知道用在哪”的焦虑。cut 是一个非常好的起点,因为它足够简单,也足够有用,能让你快速建立对管道和文本流的直觉。
7. 关于 cut 与国产化环境的一点个人心得
最近两年我开始接触越来越多的国产化环境下运行的运维场景,包括基于 OpenAnolis、openEuler、麒麟等发行版的工作环境。这些系统的内核和核心命令实现,基本都保持了对 GNU coreutils 的高度兼容,cut 命令无论在参数行为还是输出格式上,与经典 Linux 环境几乎没有差异。这一点让我比较安心,也就意味着你在标准环境里练熟的 cut 技能,几乎可以无缝迁移到国产化服务器上。
当然也要提醒一句:部分国产化系统可能默认 shell 是 bash,但存在 dash 作为系统脚本解释器的情况,在 dash 环境下,echo 的转义行为和变量处理的差异可能影响脚本,但 cut 本身很少受影响。如果要在脚本里使用 cut,建议写清楚绝对路径 /usr/bin/cut 或者依赖 command -v cut 检查。跨平台部署时,这些小细节比命令本身更容易让你踩坑。
8. 最后留一个实用的小技巧
如果我只能分享一条关于 cut 的个人经验,那就是:处理管道数据时,先把 cut 放到最前端,让它尽早削减数据量。 很多人习惯先把日志经过 grep 过滤,再用 cut 提取,这当然没错;但反过来,先用 cut 去掉无关字段,再让 grep 面对更短的文本,往往更高效。尤其是当你要对多个字段做多轮条件组合时,预先把大宽表“瘦身”成小宽表,能极大提升后续命令的速度和可读性。
另外还我常配合 cut 使用的一个小连招是:在确认文件格式前,先用 head -n 5 file | cat -A 看一眼分隔符和行尾符号,再动手写命令。别看这一步简单,它能帮你挡住至少一半的分隔符问题。到现在我处理任何新的文本格式,这个习惯都没丢过。
