三剑客-sed与sort

3966 字
20 分钟
三剑客-sed与sort

三剑客-sed&&sort#

[TOC]


01.sed#

概念#

增删改查都会点,

**流编辑器:**就是有许多数据,源源不断的流入编辑器中.一行一行处理.


格式#

  • 加上-n后

  • 小p就行 3p

sed -n#

📌 -n 选项:取消默认输出,因为sed默认全部输出.

  • 通常用在查找**,模糊匹配上面**
    • 有选择的进行过滤, 不让它全部显示
  • 删除, 用不到, 没有意义
    • 为了显示删除后全部内容
    • 这个点, 我们在后面d删除的时候进行详细的演示.**
    • -n 通常配置p 来进行选择性的打印输出.


1)查找#

小p就行 3p

, 逗号#

,逗号表示范围

如果你想要打印前三行,你可以用范围打印, 选择性过滤,记得带选项-n.

$p#

📌 sed -n ‘$p’ file

  • $本身就表示末尾
  • 表示文件的最后一行

案例.输出文件的最后一行

Terminal window
[root@oldboyedu ~]# sed -n '$p' /etc/passwd
operator:x:11:0:operator:/root:/sbin/nologin
# 输出文件的最后一行
'$本身就表示末尾,表示文件的最后一行'

案例.找出文件第3行到最后一行

Terminal window
[root@oldboyedu ~]# sed -n '3,$p' /etc/passwd
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
# 找出文件第3行到最后一行

; 分号隔开#

表示同时进行.

同时,如果你不想用范围,只是想要单纯的打印没有规律的三行,比如打印 2,6,7, 行.

不能用 , 逗号

Terminal window
[root@oldboyedu ~]# sed -n '2p;6p;7p' passwd.txt
# ;分号隔开没有规律的行

sed ’/ /p’ 模糊过滤#

  • grep ‘过滤的内容’ file
  • sed -n ‘/过滤的内容/p**’ file**
    • **/ / **这是一个地址范围(address).用于匹配正则, -r匹配扩展正则
    • **p **这是命令,表示打印匹配的行.
    • 必须是这个语法才行, ’/ /p’ 否则无法进行模糊匹配

注意:sed 默认全部输出,除非使用 -n 选项抑制默认输出,然后用 p 显式打印

分别用grep和sed过滤以root开头的行.

案例.找出bash结尾的行

Terminal window
[root@test /home]# sed -n '/bash$/p' passwd.txt
root:x:0:0:root:/root:/bin/bash
# 找出以bash结尾的行

**’/ / Ip’(忽略大小写)#

  • **I ** 大写这是标志(flag),表示忽略大小写.
Terminal window
[root@oldboyedu ~]# sed -rn '/permitrootlogin|usedns/Ip' /etc/ssh/sshd_config
# I 大写, 忽略大小写

区间范围匹配#

📌 sed -n ‘/从哪里来/,/到哪里去/p’

  • 逗号隔开

案例:从adm的行到sync的行

Terminal window
[root@test /home]# sed -n '/adm/,/sync/p' passwd.txt
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
# 从adm的行到sync的行, 逗号隔开表示范围

企业中经常匹配日志

Terminal window
[root@oldboyedu ~]# sed -n '/Nov 10 09:54:32/,/Nov 10 10:13:27/p' /var/log/messages
# 按照日志中的时间进行过滤
  • 注意匹配的是日志中存在的时间, 你的日志得有这个时间.
  • 注意如果匹配的区间范围只找到开头、则输出从开头的后面所有内容
    • 如果有两个开头, 则两个开头后面的内容都输出!
  • 如果匹配到一个开头.两个结尾.
    • 则以第一个结尾为准输出内容、后面的结尾无效

sed -r#

扩展正则用-r选项

案例.过滤包含root或者adm的行,-r支持扩展正则

Terminal window
[root@oldboyedu ~]# sed -rn '/root|adm/p' passwd.txt
root:x:0:0:root:/root:/bin/bash
adm:x:3:4:adm:/var/adm:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
# | 表示或者, 扩展正则

2)替换#

s# # #g#

  • 前两个#号之间可以用正则**.
  • 我们也可以在**#号**前面加 / / 进行正则匹配.
    • **/ / **这是一个地址范围(address).用于匹配正则, -r匹配扩展正则
    • ‘3,5s#:#@#g’ #把第3行到第5行的所有:替换为@
    • ‘/root/s#bash#oldboy#g’ #把包含root行的所有bash替换为oldboy
    • 这两条命令下面都有案例
  • 因为可以支持正则,还有扩展正则,所以常跟-r选项.
    • -n 通常配置p 来进行选择性的打印输出.
    • -n ‘1s#root#oldboy#gp’ #1就是第1行,符合并输出.
      • 这两个命令,后面也有案例
    • -n ‘1p;10s#root#oldboy#gp’ #只过滤第1行和第10行,把替换后的输出
      • ;分号没有规律的两行 n,m逗号从第n行到第m行
  • 中间是三个成对的东西,可以是###,当然也可以是@@@,都可以的.
    • 灵活一点,
  • g global 全局替换,这一行中把所有匹配到的内容都进行替换否则只替换每一行的第一个匹配到的内容.

案例.将:替换成@

Terminal window
[root@oldboyedu ~]# sed 's#:#@#g' passwd.txt
root@x@0@0@root@/root@/bin/bash
bin@x@1@1@bin@/bin@/sbin/nologin
daemon@x@2@2@daemon@/sbin@/sbin/nologin
# 有g则是全局替换
'将:替换成@'

案例.如果不加g全局替换, 则替换的是每行的第一个:

Terminal window
[root@oldboyedu ~]# sed 's#:#@#' passwd.txt
root@x:0:0:root:/root:/bin/bash
bin@x:1:1:bin:/bin:/sbin/nologin
daemon@x:2:2:daemon:/sbin:/sbin/nologin
adm@x:3:4:adm:/var/adm:/sbin/nologin
# 没有g就是只替换匹配到的第一个:
'不加g全局替换, 则替换的是每行的第一个'

案例.把第3行到第5行的所有:替换为@

案例.把包含root行的所有bash替换为oldboy 这个使用 / / 进行正则匹配

sed -i#

⚠️ 如果没有加 -i这个选项,就只是给你看一下修改后的内容,但其实原文件并没有真正的替换.

Caution

当-i和-r(扩展正则)组合时 这个-i选项要最后用, -ri 才是正确的 -ir 是错误的

  1. 只有加上-i后, 源文件才是真正的修改了.
  2. 因为以后修改配置文件的时候先不要着急修改,先看看没有问题后再加上-i选项.否则容易修改错误.

-i.bak#

先备份成.bak;再对原文件进行修改

  • 如果原文件特别多,那你就要进行压缩备份了
Terminal window
[root@Rocky10 ~]# cat hh.txt
hhh
fadfa
# 原文件的内容
[root@Rocky10 ~]# sed -i.bak 's#hhh#xxx#g' hh.txt
# 我们实际修改的是原文件的内容
[root@Rocky10 ~]# cat hh.txt
xxx
fadfa
# 原文件改变
[root@Rocky10 ~]# cat hh.txt.bak
hhh
fadfa
# 这个是备份文件
✅️ -i.bak 先备份再修改

3)后向引用#

  • 处理其中某一部分
Important

因为 ( ) 为扩展正则,所以要用sed -r 选项

案例.在第二部分加上<和>

最后一个$写括号()里面或者外面都是可以的.

  • 不要记-nr 你只需要知道 这-n和后面的p是紧密结合在一起的
    • 表示不输出全部,而是把过滤出来的内容进行打印输出.
  • 这个-r对应的是扩展正则,
Terminal window
[root@oldboyedu ~]# ifconfig ens33 | sed -n '2p' | sed -r 's#^.*et (.*) ne.*k (.*) br.*t (.*)$#网络为:\1 子网掩码为:\2 广播地址为:\3#g'
# 分步版
[root@oldboyedu ~]# ifconfig ens33 | sed -rn '2s#^.*et (.*) ne.*k (.*) br.*t (.*)$#网络为:\1 子网掩码为:\2 广播地址为:\3#gp'
# 结合版 ✅️ 一行搞定

取IP地址#

显示指定网卡,

Terminal window
[root@oldboyedu ~]# ip addr show ens33
# 显示指定网卡信息

当然还有更加简便的方法去查看你的ip地址.

用 hostname -I #但是这个IP后面是有一个空格的.别忘

配合bash#

📌 将屏幕上的字符串,当命令使

Terminal window
[root@oldboyedu ~]# echo oldboy | sed -r 's#ol(.*)boy#mkdir \1#g'
mkdir d
# 输出的字符串是 mkdir d
[root@oldboyedu ~]# echo oldboy | sed -r 's#ol(.*)boy#mkdir \1#g' | bash
# 相当于执行了 mkdir d, 管道给bash后变为命令执行
[root@oldboyedu ~]# ll
drwxr-xr-x 2 root root 6 Nov 10 11:45 d
# 成功创建了d这个目录
✅️ 将sed处理后的字符串通过管道传给bash执行
Terminal window
[root@oldboyedu ~]# seq 3
1
2
3
[root@oldboyedu ~]# seq 3 | sed -r 's#(.*)#\1#g'
1
2
3
# 全部都匹配出来,并且拿出来
[root@oldboyedu ~]# seq 3 | sed -r 's#(.*)#touch \1#g'
touch 1
touch 2
touch 3
# 如果后面再配合 | bash 就会把这些当做命令执行

4)d删除#

Important

d 删除 因为可以支持正则,还有扩展正则,所以常跟-r选项 删除, 不使用-n选项. 没有意义 -n 通常配置p 来进行选择性的打印输出

d删除是这个样子,替换也是一样的

指定行#

  1. 删除第三行!

  1. 删除第一行至第三行

识别不出来 - 只能用 , 逗号进行分隔

  1. 删除没有规律的行,

; 分号 #表示同时进行.

不能用 , 逗号

Terminal window
[root@oldboyedu ~]# sed '2d;6d;7d' passwd.txt
# ; 分号表示同时进行, 删除没有规律的行

模糊过滤删除#

案例.删除包含root的行

Terminal window
[root@oldboyedu ~]# sed '/root/d' passwd.txt
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
# 删除包含root的行

案例.删除包含root或者adm的行

Terminal window
[root@oldboyedu ~]# sed -r '/root|adm/d' passwd.txt
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
# -r 扩展正则, | 表示或者

案例.删除以nologin结尾的行

Terminal window
[root@oldboyedu ~]# sed '/nologin$/d' passwd.txt
root:x:0:0:root:/root:/bin/bash
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
# 正常的$正则, 非常简单

案例.按区间范围删除

Terminal window
[root@oldboyedu ~]# sed '/adm/,/halt/d' passwd.txt
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
# 和上面的查找, 差不多!

~ 隔行删除#

  1. 行号从 1 开始计数

1. 删除所有奇数行(保留偶数行)#

Terminal window
[root@oldboyedu ~]# sed '1~2d' filename
# 1~2:从第1行开始,每隔2行(即第1、3、5...行)
# d:删除

2. 删除所有偶数行(保留奇数行)#


5)增加#

c a i#

Terminal window
# 要使用 sed 在指定行下面添加一行,应该使用 a 命令
[root@oldboyedu ~]# sed -i '/use_pty/a\Defaults editor=/usr/bin/vim, env_editor' /etc/sudoers
# /符合匹配的行/ # 用来过滤
# a # 在这个指定行后面追加内容
# \ # 转义字符, 告诉 sed 后面紧跟的文本是命令的一部分, 命令没有结束
Note
Terminal window
sed '3a lidao996' 文件名
# 后面这个lidao996是一个字符串, 中间隔着一个空格
sed '$a 1234' 文件名
# 在最后一行追加,1234 这里的$不是正则,表示最后一行的行数,是个数字
sed -r '/正则/c lidao996' 文件名
# / / 是一个地址范围(address), 用于匹配正则
# c a i 是命令, 表示增加后面字符串这一行
# 空格隔开, 后面紧跟字符串

下面我们 c a i 前面跟的都是数字,其实他不止可以跟数字.还可以跟正则,

  • 对于c a i来讲,-n选项可以加,也可以不加
    • 为了全局可观看 一般不加-n

3c整行替换重点!

案例.免交互将selinux关闭

Terminal window
[root@oldboyedu ~]# sed -i '8c SELINUX=disabled' /etc/selinux/config
# c 整行替换, 免交互关闭selinux

🧣w(保存到新文件)#

  • 保存到新文件中,如果没有将新建一个
  • 如果有则覆盖里面的内容.
    • 后面两次案例内容全部保存在1.txt中.
  • 它也可以使用前面的正则. / / 包裹起来
  • 也可以加**-r选项, 匹配扩展正则.**
  • 通常配合-n使用,取消默认输出
  • 注意引号位置,同时把文件名也包含在内!
  • 这个新文件也可以使用绝对目录进行保存

案例.将第3行的内容保存到1.txt中

Terminal window
[root@oldboyedu ~]# sed -n '3w 1.txt' passwd.txt
# w 后面跟保存到的文件名, 将第3行的内容保存到1.txt中

案例.将包含root或者sync的行,继续保存在1.txt中

Terminal window
[root@oldboyedu ~]# sed -rn '/root|sync/w 1.txt' passwd.txt
# -r 扩展正则, w 保存到文件, 会覆盖之前的内容

在最后一行追加内容#

在Linux中使用sed命令在文件er.txt最后一行追加内容1234正确的命令是:

Terminal window
[root@oldboyedu ~]# sed '$a 1234' er.txt
# $:表示最后一行
# a 1234:在匹配行之后追加内容1234

其他解决方案:

直接用 echo ‘1234’ >> re.txt

直接追加到文件最后.


6)sed总结#

  • 下面这个是不含#的行,并不是 以#开头的行.


02.sort 排序#

  • sort 文件
  • 其他命令 | sort
    • 默认按照首个**字母(或者数字)进行排序.
    • 默认升序排序,从小到大进行排序.
参数说明
-n按照数值大小进行排序, 如果是数字必须加这个选项, 否则只会按照首个数字升序排
-r逆序排序(降序)
-k指定列排序

案例.排序oldboy.txt 文件

Terminal window
[root@oldboyedu ~]# cat oldboy.txt
test
oldboy
shell
linux
tab
shazi
[root@oldboyedu ~]# sort oldboy.txt
linux
oldboy
shazi
shell
tab
test
# 默认按照首个字母进行排序, 升序

案例.对数字的文件进行排序

Terminal window
[root@oldboyedu ~]# cat n.txt
1
2
20
100
5
500
8
[root@oldboyedu ~]# sort n.txt
1
100
2
20
5
500
8
# 按照首个数字大小进行排序(升序)
'不加-n选项, 只会按首位数字排序, 所以1后面是100'

案例.按照数字进行排序

Terminal window
[root@oldboyedu ~]# sort -n n.txt
1
2
5
8
20
100
500
# 按数值的大小, 升序排
'加了-n后, 就是真正的数值大小排序了'

案例.按照数字的逆序排序

Terminal window
[root@oldboyedu ~]# sort -rn n.txt
500
100
20
8
5
2
1
# 多了一个-r选项, 降序排

📌 sort -rn 是 Linux 中用于按数值降序排序的强大命令组合.

案例.指定列进行排序 -k2 表示第2列

按照第2列的数字进行正序排序

  • 升序,从小到大排
Terminal window
[root@oldboyedu ~]# sort -nk2 n.txt
zs 1
ls 2
dd 8
lw 20
ed 90
gs 100
td 500
# -k2 指定第2列, -n 数值升序

案例.按照第二列的数字进行逆序排序

Terminal window
[root@oldboyedu ~]# sort -rnk2 n.txt
td 500
gs 100
ed 90
lw 20
dd 8
ls 2
zs 1
# 多加一个-r选项, 降序

03.uniq 去重统计#

  • -c #去重并统计个数
    • 将统计的个数,放在第一列
    • 随机排序,并非升序
      • 所以后面再继续跟sort -rn
      • 按照数字的大小进行降序排序
    • 即使只出现一次也会被统计出来.

⚠️ ①先排序②再去重

  • 只能去紧挨着的重复的数据
  • 如果不紧挨着,没有办法去重
  • 所以我们要先进行排序
Terminal window
[root@oldboy home]# cat oldboy.txt
test
shell
test
linux
shell
docker
[root@oldboy home]# cat oldboy.txt | uniq
test
shell
test
linux
shell
docker
# 直接去重,并没有成功,只能去紧挨着的重复的数据
Terminal window
[root@oldboy home]# cat oldboy.txt | sort
docker
linux
shell
shell
test
test
# 排完后, 就紧挨着了
[root@oldboy home]# cat oldboy.txt | sort | uniq
docker
linux
shell
test
# 这次成功去重了

案例.去重并统计个数

Terminal window
[root@oldboyedu ~]# cat oldboy.txt | sort | uniq -c
1 docker
1 linux
2 shell
2 test
# 加上-c选项, 第一列显示出现次数
# 出现一次也会统计

案例.统计当前passwd.txt中每个单词出现的次数从大到小排序

  • 先用sed 替换,把数字和特殊字符都换成空格
  • 这个原理是把特殊字符啥的,都换成空格,
  • 然后配合xargs -n1 把内容按照1列进行输出.

Terminal window
[root@oldboy home]# cat passwd.txt | sed -n 's#[0-9:/x]# #gp' | xargs -n1 | sort | uniq -c | sort -rn
12 sbin
6 nologin
5 bin
4 root
3 var
3 sync
3 shutdown
3 mail
3 halt
3 adm
2 spool
2 operator
2 lp
2 daemon
1 lpd
1 bash
# 先把数字和特殊符号, 都替换为空格
# 接着xargs -n1 排成一列输出
# sort | uniq -c | sort -rn

再来看一种方法:

  • egrep 进行过滤完后, -o 把单词按照1列都过滤出来, 再跟 | sort | uniq -c |sort -rn
    • ‘[a-Z]+’ #把连续的字母(单词)都过滤出来
    • -o 的作用和 xargs -n 作用一样,变成一列输出.

我们也可以把这个单独的x去除掉

案例.对文件中任意字符进行统计、取出top10

Terminal window
[root@oldboy home]# grep -o '.' passwd.txt | sort | uniq -c | sort -rn | head
60 :
37 n
35 /
33 o
26 i
21 s
18 b
17 l
17 a
12 t
# -o 输出过程, '.' 匹配任意字符
# sort | uniq -c | sort -rn 统计并降序
# head 取前10

典型工作流示例#

Terminal window
# 分析网站访问日志:统计IP访问量并排序
[root@oldboy home]# awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10
1428 192.168.1.23
1205 10.0.0.45
983 172.16.5.201
# ... (TOP 10 IPs)

统计每一个字符(单词)出现的次数,并取出次数最多的前10位.#


04.wc#

📌 统计文件内容

🔹 默认输出(不加选项)

Terminal window
[root@oldboyedu ~]# wc file.txt
10 50 300 file.txt
# 输出依次为:行数(lines) 单词数(words) 字节数(bytes) 文件名

🔹 常用选项

选项说明
-l只统计行数(lines)
-w只统计单词数(words)
-c只统计字节数(bytes)

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

三剑客-sed与sort
https://www.kpyun.fun/posts/basics/core/core12/
作者
久棹
发布于
2025-08-08
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
久棹
只要胆子大,天天寒暑假!
公告
欢迎来到久棹的技术小站!本站专注 Linux 运维学习笔记分享,如有问题欢迎交流探讨 🎉
分类
标签
站点统计
文章
98
分类
11
标签
203
总字数
244,453
运行时长
0
最后活动
0 天前
站点信息
构建平台
Local
博客版本
Firefly v6.13.5
文章许可
CC BY-NC-SA 4.0

文章目录