news 2026/9/12 19:10:49

Linux文本处理四件套:cut、sed、awk、sort实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux文本处理四件套:cut、sed、awk、sort实战指南

接手 Linux 服务器时间长了,你会发现一个特别有意思的现象:很多看起来复杂得要命的问题,最后查来查去,都落到几个最基础的命令上。尤其是处理日志、清洗数据、批量改配置这种活儿,cutsedawksort这四个命令只要玩得转,效率能甩开别人好几条街。我甚至觉得,把这四个命令练熟了,比背一堆花哨的脚本框架都管用。

这篇文章不打算按部就班地讲手册,而是直接按“实际干活”的思路来拆。我会把这四个命令放在具体场景里,告诉你什么时候用哪个、它们怎么配合、哪些坑是我踩过的。不管是刚接触 Shell 的新手,还是想系统梳理一遍的老手,跟着走一遍,应该都能有收获。

1. 四个命令的分工与合作:先搞清楚谁干什么

很多人学这四个命令,是分开一个个学的,学完还是懵。我的建议是反过来:先看它们的分工,再去看语法,你会突然发现思路清晰很多。

把它们想象成一条手工流水线:cut负责“切”——按列把一行数据劈开;sed负责“改”——对文本流做替换、删除、插入;awk负责“算”——它能把文本按字段拆开,然后做统计、格式化、甚至写简单的判断逻辑;sort负责“排”——把结果按你要的顺序输出,顺便还能配合uniq去重计数。

分工清楚之后,组合起来威力就出来了。举个例子,线上 Nginx 日志想统计“哪个 IP 访问最频繁”,你大概率会这样写:

awk '{print $1}' access.log | sort | uniq -c | sort -nr | head -20

这段命令里,awk负责把第一列 IP 提出来,sort负责把相同 IP 排到一起,uniq -c数出每个 IP 出现次数,再交给sort -nr按数字倒序排,最后head取前 20。整个过程没有任何一个命令是多余的,就是流水线作业。

所以我的建议是:不要孤立地记参数,而是把它们串在一条“处理链”里理解。下面逐个拆开讲,但我更希望你关注它们各自的“边界”——每个命令擅长什么、不擅长什么。

2. cut 基本功:列提取不是只会 -d -f 就完事了

cut是四个命令里最“简单粗暴”的一个。它的核心思路就是按列切,语法不复杂,但正因为简单,很多人用的时候容易卡在一些细节上。

2.1 三个核心参数:-d、-f、-c

  • -d指定分隔符,默认是制表符TAB
  • -f指定取第几列(字段)
  • -c按字符位置切,而不是按分隔符切

最常见的组合是-d-f。比如/etc/passwd文件用冒号分隔,想提取用户名和登录 Shell:

cut -d: -f1,7 /etc/passwd

这里-f1,7表示取第 1 列和第 7 列,输出出来就是“用户名”加“登录Shell”的对照表。如果要取连续的几列,可以写成-f1-3-f1-3,5,逗号用来做集合。

-c用的场景相对少,但很合适处理固定宽度的文本。比如某些程序输出的日志,每一行的前 10 个字符是时间戳,你想单独把时间戳抠出来:

cut -c1-10 app.log

这就把每行的第 1 到第 10 个字符切出来了,不需要管分隔符是什么。

2.2 cut 的两个明显局限

cut要清楚它的短板,否则会在某些场景下浪费大量时间。

第一个局限:它默认不支持“按多个连续空格做分隔符”。比如你用ps aux查看进程,输出里各列之间是多个空格,这时候你cut -d' ' -f2大概率拿不到 PID,因为列之间空格的个数不固定。正确的做法要么先tr -s ' '压缩空格,要么直接改用awk。所以我自己用cut时,基本都是处理分隔符明确的文件,比如配置文件、CSV、/etc/passwd这类;一旦遇到多空格对齐的文本,直接换成awk更省事。

第二个局限:cut不能重排列顺序。它只能按原顺序输出你选中的列,没法像awk那样任意调整列的前后位置。想“把第三列放第一列”这种操作,cut是干不了的。

2.3 实际场景:批量提取系统用户信息

我经常在排查服务器账号的时候用这样一个组合:

cut -d: -f1,3,7 /etc/passwd | awk -F: '$2 >= 1000 {print}'

先用cut把用户名、UID、Shell 提出来,再用awk过滤 UID 大于等于 1000 的普通用户。这里cut负责“提”,awk负责“筛”,分工很清晰,代码也一眼能看懂。

3. sed 进阶:不只是替换,它是一把文本手术刀

sed被称作“流编辑器”,它最大的特点是:一行一行地读入数据,处理完再输出。这种“一边读一边处理”的模式,让你可以处理超大文件,不用担心一次性载入内存的问题。

3.1 核心用法:替换、删除、打印

入门建议从最常用的三个动作开始:

  • s:替换
  • d:删除
  • p:打印

替换是最常见的使用方式。比如把配置文件里所有localhost改为127.0.0.1

sed -i 's/localhost/127.0.0.1/g' config.ini

这里-i表示原地修改,g表示替换每一行中所有匹配的地方,而不是只替换第一个。很多新手第一次用sed -i时容易紧张,生怕把文件改坏。我的建议是:先用不加-i的命令看输出,确认没问题后再加-i落地。

删除场景也很常用。比如想去掉/etc/ssh/sshd_config里的注释行和空行,方便看有效配置:

sed -e '/^#/d' -e '/^$/d' /etc/ssh/sshd_config

-e表示连续执行多条 sed 指令,这里是把“以 # 开头”和“空行”都删掉。

打印模式要结合-n来理解。sed -n '5p' file.txt的意思是不输出所有行,只打印第 5 行。这有点像head -5 | tail -1,但写法更简洁,而且能指定多个行号或范围:

sed -n '10,20p' app.log

这就把第 10 到第 20 行打印出来了,非常适合从日志里截取一段上下文。

3.2 定址与正则:sed 灵不灵活全靠它

sed的命令前面可以加“地址范围”,指定要处理哪些行。这个功能非常强大,我举两个典型的场景。

第一个是根据行号范围。比如你只想修改前 10 行里的某个内容:

sed -i '1,10s/foo/bar/g' test.txt

第二个是根据正则匹配范围。比如你只处理包含ERROR的行:

sed -i '/ERROR/s/DEBUG/INFO/g' app.log

这条的意思是:只在包含ERROR的行里,把DEBUG替换成INFO。这种“按内容定址”的方式,在处理日志时特别好用。

还有一个很经典的范围用法——从某个标记开始,到另一个标记结束:

sed -n '/BEGIN/,/END/p' data.txt

这种写法适合从结构化文件里抽取片段,比如提取一段配置块。

3.3 关于 -i 的注意事项

sed -i在不同系统上行为略有差异。Linux 上直接sed -i 's/.../.../' file就行;但如果你是 macOS,建议写成sed -i '' 's/.../.../' file,因为 macOS 的sed要求-i后面必须跟一个备份后缀,如果不给后缀就必须写一个空字符串。这一点跨平台写脚本时特别容易踩坑。

另外,我习惯在做批量修改之前先备份:

cp app.conf app.conf.bak sed -i 's/old/new/g' app.conf

说白了,sed -i是“原地修改”,它是在原文件上操作的。默认情况下它不是原子的,一旦中途出问题,文件可能处于半修改状态。备份是个好习惯,尤其在生产环境操作时。

4. awk 核心逻辑:它本质是一个微型编程环境

如果说sed是手术刀,awk就是一台小型加工中心。它太强了,很多人学了多年也只是用了皮毛。但我觉得,掌握它的核心模型之后,大部分日常工作场景就足够了。

4.1 awk 的工作模型:BEGIN、主循环、END

awk处理文件时,本质上分三个阶段:

  1. BEGIN块:读文件之前执行,通常用来初始化变量、设置分隔符
  2. 主循环:逐行读入,按你写的条件处理每一行
  3. END块:所有行处理完之后执行,通常用来输出汇总结果

这个模型理解后,很多 awk 脚本你就看得懂了。比如我想统计一个文件的总行数和总字节数:

awk '{lines++; chars += length($0)} END {print lines, chars}' file.txt

主循环里逐行累加,END里输出结果。这就是 awk 典型的“统计器”写法。

4.2 内置变量:NF、NR、FS、OFS

awk有四个内置变量,你必须了然于心:

  • NF:当前行的字段数
  • NR:当前处理到第几行
  • FS:输入字段分隔符,默认是空格
  • OFS:输出字段分隔符,默认也是空格

配合NF可以快速筛选格式异常的行。比如一个 CSV 文件,正常每行应该有 5 个字段,但有些行多了一列,想快速找出来:

awk -F',' 'NF != 5 {print NR": " $0}' data.csv

这条命令会把所有字段数不等于 5 的行打出来,并标上行号,排查脏数据非常方便。

OFS的作用体现在修改输出格式。举个例子,把/etc/passwd的冒号分隔改成逗号分隔:

awk -F: 'BEGIN{OFS=","} {print $1, $3, $7}' /etc/passwd

-F:是控制输入分隔符,OFS=","是控制输出分隔符,一进一出,格式就变了。

4.3 printf 格式化:让输出变成对齐的表格

awk里的printf是格式化输出神器,特别适合生成对齐的报表。比如我想输出“用户名、UID、HOME”三列,且对齐得整整齐齐:

awk -F: 'BEGIN{printf "%-20s %-8s %-30s\n", "USER", "UID", "HOME"} {printf "%-20s %-8s %-30s\n", $1, $3, $6}' /etc/passwd

这里%-20s表示左对齐并占满 20 个字符宽度,数字-8-30同理。这样输出的内容即使数据长短不一,排版也像表格一样整齐,看配置文件、导出报告时体验特别好。

4.4 条件与统计:awk 的拿手好戏

awk 非常善于做“带条件的统计”。比如一段接口日志,想统计“状态码为 500 的请求次数”和“平均响应时间”:

awk '$9 == 500 {count++; sum += $NF} END {print count, sum/count}' access.log

这里$9是状态码所在列,$NF是最后一列(假设是响应时间),awk 会自动把字符串转成数字进行运算。如果 count 为 0,sum/count会报“除以 0”错误,实际写脚本时建议加个判断,这是后话。

awk 还有一个常用技巧:按多个条件分组统计。比如按状态码分组,统计每个状态码的出现次数:

awk '{codes[$9]++} END {for (code in codes) print code, codes[code]}' access.log

这里用了一个关联数组codes,下标是状态码,值是次数。END循环遍历打印。这个模式可以扩展到任意维度,比如按访问路径、按 IP 段、按小时统计等等。

5. sort 不只是排序:参数选不对,结果差很远

sort看起来最简单,实际上坑最多。很多“排序结果不对”的线上问题,往往不是数据有问题,而是参数用错了。

5.1 必须掌握的五个参数

  • -n:按数字排序,不是按字典序
  • -r:倒序
  • -t:指定分隔符
  • -k:指定按第几列排序
  • -u:去重

这里重点说一说-n。用sort file.txt默认是按字典序排的,如果你文件里是一堆数字,比如10, 100, 2, 25,按字典序排出来是10, 100, 2, 25,这显然不符合直觉。想按数值大小排,必须加-n

sort -n numbers.txt

这件事看似基础,但在脚本里查 Top 日志时特别容易忘记。比如sort -k3 filesort -k3n file结果是完全不同的,前者按第 3 列字典序排,后者按数值排。

5.2 按指定列排序:-t 和 -k 组合

-t-k一般是配套使用的。比如文件内容是这样的:

alice 90 bob 75 carol 88

想按第二列的分数从高到低排:

sort -t' ' -k2 -nr scores.txt

注意这里-t指定分隔符为空格,-k2指定按第二列排序,-nr一起写表示“按数字倒序”。输出结果就是分数高的排前面。

-k还能指定多级排序规则。比如想先按第一列排,再按第二列排:

sort -t',' -k1,1 -k2,2n data.csv

这种写法在生产环境处理表格数据时非常实用。

5.3 sort 与 uniq 配合去重计数

sortuniq是天然搭档,原因在于uniq只能处理“相邻的重复行”,所以去重前必须先排序,让相同的行聚在一起。

最常见的场景是统计日志里的 IP 访问次数:

sort access.log | uniq -c

这会把每一行去重,并在每行前面加上出现次数。如果要按访问次数排序,再加一层sort -nr就行。

特别提醒一个细节:uniq -c输出的第一列是“次数”,所以如果你想按次数倒序取 Top 10,对应的命令是:

awk '{print $1}' access.log | sort | uniq -c | sort -nr | head -10

这里的第二次sort -nr就是针对uniq -c的结果做数值倒序排序。

6. 综合实战:用四个命令完成日志分析与报告输出

前面把四个命令分开讲了,但真正干活的时候,它们往往是一条流水线。下面我用一个完整的实战场景来演示怎么串联。

6.1 场景说明

假设你维护一个 Web 服务,日志文件access.log每行的格式是:

192.168.1.10 - - [12/Oct/2024:08:30:15 +0800] "GET /api/user/list HTTP/1.1" 200 5321 0.045 192.168.1.21 - - [12/Oct/2024:08:31:02 +0800] "POST /api/login HTTP/1.1" 500 1024 0.302 ...

你接到一个任务:写一条命令,统计出当天访问量排前 10 的接口路径,并输出它们的访问次数和平均响应时间。

6.2 第一步:用 awk 提取字段并格式化输出

首先,每个日志行的请求行在$7(假设以空格分隔),状态码在$9,响应时间在$NF。我想把“路径、状态码、响应时间”三个字段提出来,供后续处理:

awk '{print $7, $9, $NF}' access.log

这一步输出大概长这样:

/api/user/list 200 0.045 /api/login 500 0.302 ...

6.3 第二步:用 sort 和 uniq 统计接口访问次数

提取路径列,排序后去重计数:

awk '{print $7}' access.log | sort | uniq -c

如果想按访问次数倒序,再补一个sort -nr

awk '{print $7}' access.log | sort | uniq -c | sort -nr | head -10

这就能看到访问量最大的 10 个接口路径。

6.4 第三步:用 awk 追加平均响应时间

只统计次数还不够,还想算平均响应时间。这一步适合重新回归awk,因为要同时做“分组”和“累加”:

awk '{count[$7]++; sum[$7] += $NF} END {for (path in count) printf "%-30s %d %0.3f\n", path, count[path], sum[path]/count[path]}' access.log | sort -nr -k3 | head -10

这里用两个关联数组countsum分别记录次数和总耗时,最后在END里用printf输出对齐的表格。最后一个sort -nr -k3是按第三列(平均耗时)倒序排,也可以改成按第二列(访问次数)排,取决于你想看什么维度的 Top。

6.5 第四步:用 sed 做简单过滤或脱敏

假设只想统计状态码为 200 的请求,可以在流水线开头加一层过滤:

sed -n '/ 200 /p' access.log | awk '{count[$7]++; sum[$7] += $NF} ...'

或直接在 awk 里通过条件判断过滤,都是可以的。这里用 sed 的好处是,如果后面还要做别的文本级替换,可以顺便一起处理。

四条命令依次出场,各干各的活,最后拼出一个结果。这种思路比写一个 200 行的 Python 脚本要轻快很多,而且执行效率极高。

7. 常见问题与排查技巧实录

平时工作中,四个命令的报错或“意外结果”主要集中在几个点上。我把常见的坑整理成一张速查表,都是实测过的:

现象可能原因解决办法
cut -d' '提取多空格文本列错乱分隔符是连续空格,cut 不支持改用awk '{print $N}'或先tr -s ' '
sort结果数字顺序不对没加-n,按字典序排-n,如sort -nr
sed -i在 mac 上报错或备份文件多出后缀苹果系统 sed 语法差异sed -i '' 's/.../.../' file
awk 统计时提示除以 0分组里没有匹配行,分母为 0if (count[path] > 0)判断
awk 输出列顺序不对忘记设置OFSBEGIN块里显式指定OFS
uniq -c去重失败,次数全是 1数据没有先排序sortuniq -c
多级排序时-k写错导致顺序错乱-k2默认会排序到行尾-k2,2明确“只排第二列”
sed处理含/的路径很别扭/是 sed 的默认分隔符改用#或 `

7.1 sed 替换中包含斜杠怎么办

这是新手问得最多的一个问题。比如想替换路径/usr/local/opt,直接写s//usr/local//opt/g会报错。实际上 sed 的分隔符可以换,常见做法是用#|

sed -i 's#/usr/local#/opt#g' config.sh

我一般喜欢用#做路径替换的分隔符,读起来清爽,也不用担心转义问题。

7.2 awk 取最后一列:$NF 的妙用

awk 里$NF表示当前行的最后一个字段,而NF本身是字段总数。处理长度不定的日志行时,用$NF取最后一个字段非常方便,比如取响应时间、取末尾的耗时等。如果你的行尾有空格,先awk '{$1=$1; print}'重新规范化一下,$NF才会准确。

7.3 常见问题:sort 还是 uniq 的坑

uniq检测的是“相邻行是否相同”,而不是“全文件是否相同”。所以如果你直接cat file | uniq -c,很可能发现明明有重复项,但统计次数不对。正确的姿势永远是先sort

sort file.txt | uniq -c

如果文件很大,也可以让sort使用足够的内存来优化性能,必要时用-S 1G指定排序缓冲区大小,避免磁盘 I/O 拖慢速度。

7.4 我的排错习惯

遇到这四个命令组合出来的结果不对,我一般按这个顺序排查:

  1. 先单独跑每一步,看中间输出是否符合预期
  2. 检查分隔符是什么:空格、制表符还是连续空格
  3. 检查有没有加-n,数字排序是不是按字典序了
  4. 检查-k指定的列号是否符合实际字段位置
  5. 如果是 awk 统计,加一个临时{print NR, NF, $0}看行解析是否正确

只要中间任一步的输出和预期不一致,就直接定位问题,而不是盯着整条长命令发呆。

8. 写在最后的经验之谈

四个命令单独看,每个都不难;难的是把它们“组合”成解决问题的思维习惯。我见过不少同事,一遇到文本处理就想着装 Python、写脚本,其实很多任务用 Shell 一行就能完成,而且执行效率和数据量级都够用。关键在于:先想清楚输入是什么、输出要什么、中间需要做哪几步变换,然后再去挑命令。想明白这一步,比背任何参数都重要。

还有一点:写脚本之前先在命令行手动跑一遍,确认每个环节的输出都正确,再把命令固化到脚本里。这样能避免很多“脚本一执行就报错,还不知道错在哪”的尴尬。特别是sed -i和管道组合的时候,先在测试目录里跑一次,永远是最稳妥的做法。

如果你能在实际工作中把这条“提取—处理—汇总—排序”的链路练成直觉,再去学别的命令或者更复杂的脚本语言,都会轻松很多。这四件套对我来说,就是 Linux 文本处理的基本盘。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 19:10:10

嵌入式无FPU实现二阶高通滤波器:定点近似与浮点实现性能对比

本文面向嵌入式开发者,完整讲解二阶高通滤波器的设计流程,从截止频率 1kHz、采样频率 10kHz、品质因数 0.707 的设计目标出发,推导出差分方程系数并给出可直接移植的 C 语言实现。文章重点对比浮点实现与定点近似(1+53/128、1+79/128)在精度、资源占用和适用场景上的差异,…

作者头像 李华
网站建设 2026/9/12 19:09:16

程序员职业转型指南:AI时代六种高价值路径

1. 程序员职业转型的时代背景 2023年全球技术行业裁员潮席卷了Meta、Amazon、Google等科技巨头,仅第一季度就裁减超过16万个岗位。这种剧烈震荡背后,是AI技术突破带来的生产力革命——GitHub Copilot已能自动生成40%的代码,GPT-4通过亚马逊程…

作者头像 李华
网站建设 2026/9/12 19:06:39

景区负氧离子监测系统:从数据到价值的实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 19:05:02

基于SSM框架的商场会员管理系统设计与实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 19:04:38

Claude Code驱动的Spring Boot AI平台一周MVP实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 19:02:45

15-最小生成树:Prim与Kruskal

C语言数据结构系列:最小生成树篇C语言数据结构系列(十五):最小生成树——Prim与Kruskal一、前言二、基本概念2.1 什么是生成树?2.2 什么是最小生成树?三、Prim算法3.1 思想3.2 步骤3.3 代码实现四、Kruskal…

作者头像 李华