三剑客之sed
sed(Stream Editor,流编辑器)是 Linux 运维文本处理的核心工具,用于逐行读取文本、按规则处理、输出结果,无需打开文件即可完成替换、删除、新增、提取等操作。它适合处理配置文件、日志分析、批量修改文本内容。
一、sed核心语法与工作模式
1、基础语法
sed [选项] '处理指令' 文件名
# 或从管道读取输入
cat 文件名 | sed [选项] '处理指令'
2、工作模式(核心)
sed 以行为单位处理文本,流程如下:
- 从输入中读取一行文本到内存(称为模式空间)
- 按
处理指令处理模式空间中的内容 - 输出处理后的内容到标准输出
- 重复上述步骤,直到所有行处理完毕
3、必背核心选项
| 选项 | 作用 | 运维常用示例 |
|---|---|---|
-n |
静默模式,只输出被处理的行(默认输出所有行) | sed -n '2p' test.txt(只打印第 2 行) |
-i |
直接修改原文件(危险!建议先备份) | sed -i.bak 's/old/new/g' my.cnf(备份后替换) |
-e |
执行多个处理指令 | sed -e '1d' -e 's/old/new/g' test.txt |
-f |
从脚本文件读取处理指令 | sed -f script.sed test.txt |
-r |
启用扩展正则表达式(无需转义 ()+{}) |
sed -r 's/(old1 old2)/new/g' test.txt |
-s |
将多个文件视为独立文件处理(默认视为一个整体) | sed -s 's/^/prefix:/' *.txt |
4、处理指令格式
处理指令由 地址范围 + 操作命令 组成,格式:
[地址范围]操作命令[参数]
地址范围:指定要处理的行(可选,默认处理所有行)
- 单行:
5(第 5 行)、$(最后一行) - 范围:
2,5(第 2-5 行)、1,+3(第 1 行及后续 3 行) - 正则匹配:
/error/(匹配包含 error 的行)
操作命令:核心功能(如 p 打印、d 删除、s 替换)
二、sed核心操作命令(高频)
1、打印行:p(配合 -n 使用)
作用:打印模式空间中的内容,默认会重复输出,需加 -n 静默模式。
# 打印第 3 行
sed -n '3p' /etc/passwd
# 打印第 2-5 行
sed -n '2,5p' /etc/passwd
# 打印包含 "root" 的行(正则匹配)
sed -n '/root/p' /etc/passwd
# 打印从第1行到第一个包含 "mysql" 的行
sed -n '1,/mysql/p' /etc/passwd
# 打印最后一行
sed -n '$p' test.txt
2、删除行:d
作用:删除模式空间中的行,不影响原文件(除非加 -i)。
# 删除第 1 行
sed '1d' test.txt
# 删除第 2-5 行
sed '2,5d' test.txt
# 删除包含 "INFO" 的行(过滤日志)
sed '/INFO/d' xenon.log
# 删除空行
sed '/^$/d' my.cnf
# 删除注释行(以 # 开头)
sed '/^#/d' /etc/mysql/my.cnf
# 删除最后一行
sed '$d' test.txt
3、替换内容:s(最常用)
作用:替换文本内容,核心命令,格式:
sed 's/旧字符串/新字符串/替换标记' 文件名
| 替换标记 | 作用 |
|---|---|
g |
全局替换(一行中所有匹配项,默认只替换第一个) |
i |
忽略大小写 |
p |
打印替换后的行(配合 -n) |
w |
将替换后的行写入新文件 |
基础替换示例
# 将每行第一个 "old" 替换为 "new"
sed 's/old/new/' test.txt
# 全局替换(所有 "old" 替换为 "new")
sed 's/old/new/g' test.txt
# 忽略大小写替换
sed 's/error/ERROR/gi' app.log
# 替换并只输出替换后的行
sed -n 's/old/new/gp' test.txt
# 替换并写入新文件
sed 's/old/new/gw output.txt' test.txt
# 直接修改原文件(备份为 my.cnf.bak)
sed -i.bak 's/gtid_mode=OFF/gtid_mode=ON/g' /etc/mysql/my.cnf
正则替换示例(需 -r)
# 将 192.168.1.x 替换为 10.0.0.x
sed -r 's/(192\.168\.1)\.([0-9]+)/10.0.0.\2/g' test.txt
# 批量注释配置行(在包含 "log_bin" 的行前加 #)
sed -r 's/(.*log_bin.*)/#\1/' my.cnf
# 批量取消注释(去掉 # 开头的行的注释)
sed -r 's/^#(.*)/\1/' my.cnf
使用反向引用
-
\1:引用第 1 个分组的内容(即192.168.1)。 -
.:插入一个字面量点号。 -
\2:引用第 2 个分组的内容(即 IP 的最后一段数字) -
.*:匹配任意字符(除换行符)0 次或多次。- 第一个
.*匹配log_bin前面的所有内容(如空格、制表符等)。 - 第二个
.*匹配log_bin后面的所有内容(如具体的路径或配置值)。
- 第一个
4、新增行:a(追加)、i(插入)
| 命令 | 作用 | 示例 |
|---|---|---|
a |
在指定行后追加内容 | sed '2a new line' test.txt(第 2 行后加新行) |
i |
在指定行前插入内容 | sed '2i new line' test.txt(第 2 行前加新行) |
# 在第 3 行后追加一行配置
sed '3a gtid_mode = ON' my.cnf
# 在包含 "mysql" 的行前插入注释
sed '/mysql/i # 这是 MySQL 配置' my.cnf
# 在最后一行后追加内容
sed '$a # End of file' test.txt
5、替换整行:c
作用:将指定行替换为新内容。
# 将第 2 行替换为新内容
sed '2c new line' test.txt
# 将包含 "old" 的行替换为新内容
sed '/old/c new content' test.txt
6、 读取文件内容:r
作用:将指定文件的内容插入到目标行后。
# 在第 5 行后插入 backup.txt 的内容
sed '5r backup.txt' test.txt
# 在包含 "mysql" 的行后插入配置内容
sed '/mysql/r extra.cnf' my.cnf
三、场景实战
场景 1:批量修改 MySQL 配置文件
# 1. 启用 GTID 模式(直接修改原文件,备份 .bak)
sed -i.bak -r 's/(gtid_mode=).*/\1ON/; s/(enforce_gtid_consistency=).*/\1ON/' /etc/my.cnf
# 2. 注释掉所有 log_bin 相关配置
sed -i 's/^log_bin/#log_bin/' /etc/my.cnf
# 3. 在 [mysqld] 段后添加半同步复制配置
sed '/\[mysqld\]/a rpl_semi_sync_master_enabled = 1\nrpl_semi_sync_master_wait_for_slave_count = 1' /etc/my.cnf
场景 2:批量修改 K8s YAML 文件(替换镜像版本)
# 将所有 Xenon 镜像版本从 v3.0.0 改为 v3.0.1
sed -i 's/radondb\/xenon:v3.0.0/radondb\/xenon:v3.0.1/g' mysql-cluster.yaml
# 给所有 Pod 添加资源限制(在 spec.template.spec 后插入)
sed '/spec.template.spec/a \ \ \ \ resources:\n\ \ \ \ \ limits:\n\ \ \ \ \ \ cpu: 2\n\ \ \ \ \ \ memory: 4Gi' mysql-cluster.yaml
场景 3:提取日志中的关键信息(结合正则)
# 从 Nginx 日志中提取所有访问 IP 和 URL
sed -n 's/^\([0-9.]\+\).*"\([A-Z]\+ \/.*\) HTTP.*/\1 \2/p' access.log
# 从 MySQL 慢查询日志中提取执行时间超过 10s 的 SQL
sed -n '/Query_time: [0-9]\{2,\}/p' slow.log
注意:如果加上-r扩展正则,就可以不用转义()和+
sed -rn 's/^([0-9.]+).*"([A-Z]+ \/.*) HTTP.*/\1 \2/p' access.log
^\([0-9.]\+\).*"\([A-Z]\+ \/.*\) HTTP.*
这里定义了两个捕获分组,用于提取我们想要的数据:
- 第一部分(提取 IP):
^\([0-9.]\+\)^:匹配行首。[0-9.]:匹配数字或点号。\+:匹配前面的字符 1 次或多次。- 含义:匹配行首的 IP 地址(如
192.168.1.1),并将其存入 分组 1(\1)。
- 中间部分:
.\*".:匹配任意字符。*:匹配 0 次或多次。":匹配双引号。- 含义:跳过 IP 地址和请求行之间的所有内容(通常是时间戳、时区等),直到遇到第一个双引号
"。
- 第二部分(提取请求):
\([A-Z]\+ \/.\*\)[A-Z]\+:匹配一个或多个大写字母(即 HTTP 方法,如GET、POST)。\/:匹配一个空格和一个斜杠/(URL 的开头)。.*:匹配后面的任意字符(即具体的路径,如index.html)。- 含义:匹配整个请求部分(如
GET /index.html),并将其存入 分组 2(\2)。
- 结尾部分:
HTTP.\*- 匹配后面的
HTTP/1.1等协议版本信息,确保我们只截取到路径为止。
- 匹配后面的
5. 替换内容:\1 \2
\1:引用分组 1(IP 地址)。:中间加一个空格。\2:引用分组 2(请求方法 + 路径)。
🎯 实际效果演示
假设你的 access.log 原始内容如下(标准 Nginx/Apache 格式):
192.168.1.100 - - [10/Oct/2023:13:55:36 +0800] "GET /index.html HTTP/1.1" 200 1234
10.0.0.5 - - [10/Oct/2023:13:56:01 +0800] "POST /api/login HTTP/1.1" 404 567
Invalid log line without proper format
运行命令后,输出结果为:
192.168.1.100 GET /index.html
10.0.0.5 POST /api/login
注意:第三行 Invalid... 因为不符合正则规则(没有 IP 开头或没有 HTTP 请求格式),所以被 -n 和 p 过滤掉了,没有输出。
场景 4:批量重命名文件(配合 find)
# 将所有 .txt 文件后缀改为 .bak
find . -name "*.txt" | sed 'p; s/\.txt/\.bak/' | xargs -n2 mv
# 解释:
# p → 打印原文件名;s → 替换为新文件名;xargs -n2 mv → 执行 mv 原文件 新文件
从当前目录(.)开始,递归查找所有名字以 .txt 结尾的文件。
./a.txt
./b.txt
./sub/c.txt
| sed 'p; s/\.txt/\.bak/'
这是整个命令最精彩的部分。sed 这里做了两件事:
p:打印当前行(即原文件名)。;:分号,分隔两个命令。s/\.txt/\.bak/:将当前行中的.txt替换为.bak。- 结果:对于每一个输入的文件名,
sed都会输出两行——第一行是原名,第二行是新名。 - 输出示例:
./a.txt <-- 原样打印 (p)
./a.bak <-- 替换后打印 (s/...)
./b.txt <-- 原样打印
./b.bak <-- 替换后打印
| xargs -n2 mv
xargs:将标准输入转换为参数传递给后面的命令。-n2:这是关键参数,意思是每次只取 2 个参数(即两行文本)传递给mv命令。mv:移动/重命名命令。- 执行逻辑:
xargs读取前两行:./a.txt和./a.bak。- 执行命令:
mv ./a.txt ./a.bak(把 a.txt 改名为 a.bak)。 xargs读取接下来的两行:./b.txt和./b.bak。- 执行命令:
mv ./b.txt ./b.bak。

浙公网安备 33010602011771号