Linux 文本处理三剑客之 sed:从按行打印到模式空间

一、认识 sed
1. sed 是什么
sed 的全称是 Stream Editor,也就是流编辑器。它会读取文件或管道中的文本,按照给定规则逐行处理,再把结果输出。
sed 有几个很明显的特点:
- 非交互式处理 :命令写好后自动执行,不需要像
vim一样进入编辑界面; - 以行为单位处理:默认一次读取一行,处理完成后再读取下一行;
- 支持正则表达式:可以按照文本特征选择目标行或目标内容;
- 适合批量操作:替换、删除、插入、格式整理都可以写成命令;
- 方便接入管道:可以直接处理其他命令的输出。
例如:
bash
# 只显示 /etc/passwd 中以 root 开头的行
sed -n '/^root/p' /etc/passwd
# 把命令结果中的 root 替换为 ROOT
ps -ef | sed 's/root/ROOT/g'
2. sed 的处理流程

理解 sed,最重要的不是先背命令,而是先弄清它的处理流程:
- 从输入中读取一行;
- 把这一行放入模式空间;
- 判断这一行是否符合地址条件;
- 如果符合,就执行
p、s、d、a等操作; - 默认把处理后的模式空间输出;
- 清空模式空间,再读取下一行。
这里的"地址"不是 IP 地址,而是告诉 sed:哪些行需要执行后面的操作。
地址可以是:
- 行号,例如第 5 行;
- 行号范围,例如第 2~5 行;
- 正则表达式,例如以数字开头的行;
- 正则地址范围,例如从匹配
START的行到匹配END的行。
如果没有写地址,表示对所有行执行操作。
3. 基本语法
bash
sed [选项] '地址 操作' 文件
也可以写成:
bash
sed [选项] '行数设置或正则表达式[操作]' 文件
常见操作包括:
| 操作 | 作用 |
|---|---|
p |
打印 |
d |
删除 |
s |
替换匹配内容 |
a |
在目标行后追加内容 |
i |
在目标行前插入内容 |
c |
替换整行内容 |
y |
按字符一对一转换 |
4. 常用选项
| 选项 | 说明 |
|---|---|
-n |
取消默认输出,通常和 p 配合,只打印需要的内容 |
-r |
在 GNU sed 中启用扩展正则表达式 |
-E |
启用扩展正则表达式,作用与 GNU sed 的 -r 基本相同 |
-i |
直接修改原文件 |
-i.bak |
修改原文件之前,先创建一个 .bak 备份 |
-e |
指定一条处理脚本,可用于组合多条操作 |
在不同 Unix 系统中,-E 的兼容性通常比 -r 更直观;在常见 Linux 发行版的 GNU sed 中,两种写法都可以使用。本文为了和常见 Linux 实操环境保持一致,扩展正则示例主要使用 -r。
没有把握时,先不要加
-i。先在终端预览处理结果,确认无误后再使用-i.bak修改文件。
二、准备测试文件
后面的示例统一使用 test.txt:
bash
cat > test.txt <<'EOF'
test472
linux89
python100
777apple
data42
random123
grep55
shell300
999banana
123linux
EOF
查看文件并显示行号:
bash
[root@atguigu ~]# cat -n test.txt
1 test472
2 linux89
3 python100
4 777apple
5 data42
6 random123
7 grep55
8 shell300
9 999banana
10 123linux
显示行号的好处是,后面测试第 2 行、第 5 行或某个范围时,可以直接对照原始数据。
三、打印指定内容

打印使用 p,但 p 一般要和 -n 一起使用。
1. 为什么 p 要配合 -n
直接执行:
bash
sed '5p' test.txt
sed 本身会默认输出每一行,到了第 5 行又执行一次 p,所以第 5 行会显示两次。
如果只希望显示第 5 行,应写成:
bash
sed -n '5p' test.txt
这里:
-n关闭默认输出;5表示第 5 行;p表示打印这一行。
最终只输出:
text
data42
2. 按行号打印
打印第 5 行
bash
[root@atguigu ~]# sed -n '5p' test.txt
data42
打印第 2~5 行
bash
[root@atguigu ~]# sed -n '2,5p' test.txt
linux89
python100
777apple
data42
分别打印第 2 行和第 5 行
bash
[root@atguigu ~]# sed -n '2p;5p' test.txt
linux89
data42
注意:
2,5p中的逗号表示连续范围;2p;5p中的分号表示两条独立命令。
打印奇数行和偶数行
bash
# 打印奇数行
sed -n '1~2p' test.txt
# 打印偶数行
sed -n '2~2p' test.txt
1~2 表示从第 1 行开始,每隔 2 行匹配一次;2~2 表示从第 2 行开始,每隔 2 行匹配一次。这是 GNU sed 常见写法。
打印最后一行
bash
[root@atguigu ~]# sed -n '$p' test.txt
123linux
单引号很重要。如果使用双引号,$ 可能先被 Shell 当作变量符号解析。
3. 按正则表达式打印
行号适合位置固定的文本,正则更适合内容固定、位置不固定的文本。
打印以数字开头的行
bash
[root@atguigu ~]# sed -n '/^[0-9]/p' test.txt
777apple
999banana
123linux
打印以数字结尾的行
bash
[root@atguigu ~]# sed -n '/[0-9]$/p' test.txt
test472
linux89
python100
data42
random123
grep55
shell300
打印包含 linux 或 apple 的行
bash
[root@atguigu ~]# sed -rn '/linux|apple/p' test.txt
linux89
777apple
123linux
这里的 | 属于扩展正则,因此使用了 -r。
4. 按地址范围打印
sed 不仅能按数字行号取范围,也能按照两个正则地址取范围。
先准备数字文件:
bash
seq 20 > sed.txt
打印第 3~6 行:
bash
[root@atguigu ~]# sed -n '3,6p' sed.txt
3
4
5
6
再使用正则地址:
bash
[root@atguigu ~]# sed -n '/3/,/6/p' sed.txt
3
4
5
6
13
14
15
16
为什么会多出 13~16?
因为 /3/ 的意思不是数字 3,而是"包含字符 3 的行";/6/ 也不是数字 6,而是"包含字符 6 的行"。因此:
- 第一个范围从
3开始,到6结束; - 后面
13又匹配了/3/,于是开启第二个范围; - 直到
16匹配/6/,第二个范围才结束。
如果就是要匹配内容只有 3 和 6 的行,可以把边界写完整:
bash
sed -n '/^3$/,/^6$/p' sed.txt
5. 截取一段日志
例如,从 /var/log/secure 中取出包含 10:46:21 的行,到包含 12:41:00 的行:
bash
sed -n '/10:46:21/,/12:41:00/p' /var/log/secure
统计这段内容的行数:
bash
sed -n '/10:46:21/,/12:41:00/p' /var/log/secure | wc -l
这里必须分清一个关键点:
sed并没有把10:46:21和12:41:00当作时间进行大小比较,它只是从"匹配开始文本的行"打印到"匹配结束文本的行"。
因此还要注意:
- 开始标记最好唯一,否则可能得到多段内容;
- 如果匹配到开始标记,却没有匹配到结束标记,会一直输出到文件末尾;
- 如果日志跨天,要同时考虑日期,不能只看时分秒;
- 正式操作前,先用
grep -n确认开始和结束标记是否存在。
可以先检查:
bash
grep -En '10:46:21|12:41:00' /var/log/secure
四、替换文本

替换是 sed 最常用的功能,对应命令 s,也就是 substitute。
1. 替换语法
bash
sed 's#原内容#新内容#标志' 文件
分隔符不一定要使用 /,也可以使用 #、@、| 等字符:
bash
sed 's/linux/centos/g' test.txt
sed 's#linux#centos#g' test.txt
sed 's@linux@centos@g' test.txt
三条命令的作用相同。
如果内容中本身包含大量 /,换用 # 会更清楚。例如修改路径:
bash
sed 's#/usr/local/bin#/opt/app/bin#g' app.conf
如果坚持使用 /,就需要不断转义:
bash
sed 's/\/usr\/local\/bin/\/opt\/app\/bin/g' app.conf
2. 默认只输出结果,不修改文件
把 linux 替换为 centos:
bash
[root@atguigu ~]# sed 's#linux#centos#' test.txt
test472
centos89
python100
777apple
data42
random123
grep55
shell300
999banana
123centos
再次查看原文件:
bash
[root@atguigu ~]# grep linux test.txt
linux89
123linux
原文件没有变化,因为没有使用 -i。这也是 sed 比较安全的一点:默认只是把处理结果输出到终端。
3. 使用 -i 修改文件
确认预览结果正确后,再执行:
bash
sed -i 's#linux#centos#' test.txt
此时修改会真正写入 test.txt。
生产环境更推荐先备份:
bash
sed -i.bak 's#linux#centos#g' test.txt
执行后会得到:
text
test.txt
test.txt.bak
test.txt是修改后的文件;test.txt.bak是修改前的备份。
修改前后可以对比:
bash
diff -u test.txt.bak test.txt
4. 第一处替换和全局替换
准备一行包含两个相同单词的数据:
bash
echo 'centos7 centos8' >> test.txt
不加 g:
bash
[root@atguigu ~]# sed 's#centos#linux#' test.txt | tail -1
linux7 centos8
只替换每一行中第一次匹配到的内容。
加上 g:
bash
[root@atguigu ~]# sed 's#centos#linux#g' test.txt | tail -1
linux7 linux8
这里的 g 表示全局替换,但它的准确含义是:替换每个模式空间中所有匹配,也就是每一行中的所有匹配。
5. 只替换指定行
只修改第 2 行:
bash
sed '2s#linux#centos#g' test.txt
只修改第 2~5 行:
bash
sed '2,5s#linux#centos#g' test.txt
只修改包含 linux 的行:
bash
sed '/linux/s#[0-9]#X#g' test.txt
这里可以把命令拆成两部分:
/linux/:先选中包含linux的行;s#[0-9]#X#g:再把这些行中的所有数字替换为X。
6. 在行首和行尾添加内容
^ 表示行首,$ 表示行尾,所以替换空位置就能实现追加。
在每行开头添加 INFO: :
bash
sed 's#^#INFO: #' test.txt
在每行结尾添加句号:
bash
sed 's#$#.#' test.txt
给数字结尾添加单位:
bash
printf '%s\n' 'apple 10' 'banana 5' | sed -r 's#([0-9]+)$#\1kg#'
输出:
text
apple 10kg
banana 5kg
五、删除内容
删除使用 d。
1. 删除指定行
删除第 3 行:
bash
sed '3d' test.txt
删除第 2~5 行:
bash
sed '2,5d' test.txt
删除最后一行:
bash
sed '$d' test.txt
2. 删除正则匹配的行
删除空行:
bash
sed '/^$/d' test.txt
删除空行和以 # 开头的注释行:
bash
sed -r '/^$|^#/d' /etc/ssh/ssh_config
如果配置文件中可能存在"只有空格的空白行"以及"注释符前有空格"的情况,可以写得更完整:
bash
sed -r '/^[[:space:]]*$|^[[:space:]]*#/d' /etc/ssh/ssh_config
这一条更适合真实配置文件:
^[[:space:]]*$:匹配空行或只有空白字符的行;^[[:space:]]*#:匹配行首允许有空白字符的注释行;d:不再输出这些行。
3. d 默认也不会修改原文件
执行:
bash
sed '/^$/d' test.txt
只是让空行不出现在本次输出中,原文件里的空行仍然存在。要写回文件,仍然需要 -i:
bash
sed -i.bak '/^$/d' test.txt
所以看到 d 时,不要直接理解成"磁盘上的内容已经删掉"。它首先删除的是当前处理流程中的模式空间;是否落盘,要看有没有使用 -i 或重定向到新文件。
六、追加、插入、整行替换与字符转换
1. 使用 a 在目标行后追加
在以 apple 结尾的行后添加 next123:
bash
sed '/apple$/a next123' test.txt
相关结果:
text
python100
777apple
next123
data42
在文件最后追加一行:
bash
sed '$a END' test.txt
2. 使用 i 在目标行前插入
在以 apple 结尾的行前添加 prev456:
bash
sed '/apple$/i prev456' test.txt
相关结果:
text
python100
prev456
777apple
data42
3. 使用 c 替换整行
把以 apple 结尾的整行替换为 current789:
bash
sed '/apple$/c current789' test.txt
相关结果:
text
python100
current789
data42
c 和 s 很容易混淆:
s只替换一行中匹配到的部分;c直接把目标行整体换掉。
4. 使用 y 按字符转换
bash
echo 'abc cab' | sed 'y/abc/ABC/'
输出:
text
ABC CAB
y 是逐字符一对一转换:
a转换为A;b转换为B;c转换为C。
它不是正则替换,左右字符集合的长度应保持一致。
5. 四种编辑命令的区别
| 命令 | 处理对象 | 典型用途 |
|---|---|---|
s |
行内匹配内容 | 替换单词、字段、路径、数字 |
a |
目标行之后 | 追加配置或说明 |
i |
目标行之前 | 插入标题、注释或前置配置 |
c |
整个目标行 | 重写整条配置 |
y |
单个字符映射 | 大小写或固定字符转换 |
以上命令如果没有配合 -i,仍然只是输出修改后的结果,不会直接改变原文件。
七、后向引用

普通替换适合把固定内容换成固定内容。如果需要"保留原内容的一部分,再重新排列",就要使用捕获组和后向引用。
1. 什么是捕获组
在扩展正则中,使用 () 把匹配内容分组:
text
(第一个分组)(第二个分组)(第三个分组)
替换部分可以使用:
\1:引用第一个捕获组;\2:引用第二个捕获组;\3:引用第三个捕获组。
2. 拆分并重组数字
bash
[root@atguigu ~]# echo 123456 | sed -r 's#(..)(..)(..)#\1#'
12
[root@atguigu ~]# echo 123456 | sed -r 's#(..)(..)(..)#\2#'
34
[root@atguigu ~]# echo 123456 | sed -r 's#(..)(..)(..)#\3#'
56
三个 (..) 分别捕获两位字符。重新排列引用顺序:
bash
[root@atguigu ~]# echo 123456 | sed -r 's#(..)(..)(..)#\3\2\1#'
563412
重复引用同一个分组:
bash
[root@atguigu ~]# echo 123456 | sed -r 's#(..)(..)(..)#\3\3\3#'
565656
3. 修改前缀,保留后面的内容
准备文件:
bash
cat > words.txt <<'EOF'
hello world
hello linux
good Java
good css
hello centos
EOF
把行首的 hello 改成 hi,并保留后面的内容:
bash
sed -r 's#^hello (.*)$#hi \1#' words.txt
输出:
text
hi world
hi linux
good Java
good css
hi centos
其实单纯把 hello 改成 hi,直接使用 s#hello#hi# 就够了。这里使用捕获组,是为了演示"匹配、保留、重组"的逻辑。
4. 交换两列内容
bash
sed -r 's#^([^[:space:]]+)[[:space:]]+([^[:space:]]+)$#\2 \1#' words.txt
输出:
text
world hello
linux hello
Java good
css good
centos hello
这里没有直接使用 (.*) (.*),而是使用 [^[:space:]]+ 表示一列内容。这样边界更明确,不容易因为多个空格或贪婪匹配得到意外结果。
5. 调整 /etc/passwd 字段顺序
先复制文件,不要直接操作系统原文件:
bash
cp /etc/passwd ./passwd
原始内容示例:
text
root:x:0:0:root:/root:/bin/bash
把最后一个字段放到开头,把第一个字段放到结尾:
bash
sed -r 's#^([^:]+)(:x:.*:)([^:]+)$#\3\2\1#' passwd
结果:
text
/bin/bash:x:0:0:root:/root:root
命令可以拆开理解:
^([^:]+):从行首捕获用户名,保存为\1;(:x:.*:):捕获中间内容,保存为\2;([^:]+)$:捕获最后的登录 Shell,保存为\3;\3\2\1:按照"第三组、第二组、第一组"的顺序重新输出。
6. & 表示完整匹配内容
在替换部分中,& 代表本次正则匹配到的完整内容。
给所有大写字母加括号:
bash
echo 'FTP User' | sed -r 's#[A-Z]#(&)#g'
输出:
text
(F)(T)(P) (U)ser
在 GNU sed 中,还可以结合 \U 把后续内容转换为大写:
bash
echo 'apple 10' | sed -r 's#[a-z]+#\U&#g'
输出:
text
APPLE 10
\U 属于 GNU sed 扩展,并不是所有 Unix 系统都支持。编写跨平台脚本时,要先确认目标环境。
八、模式空间与保持空间

前面的打印、替换和删除,大多只处理当前一行。要理解多行合并、临时保存和倒序输出,就需要认识模式空间与保持空间。
1. 模式空间
模式空间可以理解为 sed 当前正在处理的"工作台"。
默认流程是:
- 读取一行到模式空间;
- 在模式空间中执行
s、y、d、p等操作; - 默认输出模式空间;
- 开始下一轮,读取下一行。
前面所有行内替换,本质上都是在模式空间中完成的。
2. 保持空间
保持空间可以理解为一个额外的"临时剪贴板"。
它不会像模式空间一样自动装入当前行,而是由 h、H、g、G 等命令主动存取。保持空间初始为空,可以跨处理周期保存内容。
3. 常用命令
| 命令 | 作用 |
|---|---|
h |
用模式空间覆盖保持空间 |
H |
把模式空间追加到保持空间 |
g |
用保持空间覆盖模式空间 |
G |
把保持空间追加到模式空间 |
d |
删除模式空间,并立即开始下一轮 |
N |
读取下一行,追加到当前模式空间,中间保留换行符 |
可以用大小写来帮助记忆:
- 小写
h、g:覆盖; - 大写
H、G:追加。
其中:
h的方向是"模式空间 → 保持空间";g的方向是"保持空间 → 模式空间"。
4. 每行后添加一个空行
bash
sed 'G' test.txt
保持空间初始为空,G 会把保持空间追加到模式空间,中间带一个换行符,因此看起来像是在每一行后面增加一个空行。
5. 每两行合并为一行
准备测试数据:
bash
seq 5 > number.txt
执行:
bash
sed '$!N;s/\n/;/' number.txt
输出:
text
1;2
3;4
5
命令拆解:
$!N:如果当前不是最后一行,就再读取下一行并追加到模式空间;- 两行之间使用
\n分隔; s/\n/;/:把换行符替换成分号。
和直接写 N 相比,$!N 对奇数行文件更稳妥,最后一行会单独保留。
6. 倒序输出
bash
sed '1!G;h;$!d' test.txt
也可以关闭默认输出,只在最后一行打印:
bash
sed -n '1!G;h;$p' test.txt
第一条命令的逻辑是:
- 第一行不执行
G,直接通过h存入保持空间; - 从第二行开始,使用
G把之前保存的内容追加到当前行后面; - 使用
h把新的整体再存回保持空间; $!d表示只要不是最后一行,就删除模式空间并进入下一轮,不输出;- 到最后一行时不执行
d,默认输出已经累计完成的倒序内容。
假设文件只有三行:
text
A
B
C
保持空间会依次变成:
text
A
text
B
A
text
C
B
A
最终得到倒序输出。
不过,大文件倒序更推荐直接使用:
bash
tac large.log
使用保持空间倒序,主要是为了理解 sed 的空间交换逻辑;它需要不断累积内容,不适合把超大文件全部塞进内存。
九、常见运维用法
1. 查看有效配置
去掉空行和注释,只查看真正生效的配置:
bash
sed -r '/^[[:space:]]*$|^[[:space:]]*#/d' /etc/ssh/sshd_config
这个命令默认不会修改配置文件,只是整理显示结果。
2. 修改配置前先预览
假设要把 PermitRootLogin yes 改成 PermitRootLogin no:
bash
sed -r 's#^[[:space:]]*PermitRootLogin[[:space:]]+yes#PermitRootLogin no#' /etc/ssh/sshd_config
确认输出正确后,再备份并修改:
bash
sed -r -i.bak 's#^[[:space:]]*PermitRootLogin[[:space:]]+yes#PermitRootLogin no#' /etc/ssh/sshd_config
修改配置只是第一步,重载服务前还要检查语法。以 SSH 服务为例:
bash
sshd -t
语法检查通过后,再根据系统环境执行重载或重启。不要只看到 sed 命令成功,就认为配置一定可用。
3. 压缩连续空格
bash
sed -r 's#[[:space:]]{2,}# #g' data.txt
它会把两个及以上连续空白字符压缩成一个空格,适合整理列间空白不统一的文本。
4. 删除所有数字
bash
sed -r 's#[0-9]##g' test.txt
如果不加 g,每行只会删除第一个数字。
5. 只保留带数字的行
bash
sed -n '/[0-9]/p' test.txt
这类需求也可以使用 grep '[0-9]' test.txt。如果只是过滤,grep 更直接;如果筛选之后还要继续替换,使用 sed 更方便。
6. 给数字添加单位
准备数据:
text
apple 10
banana 5
orange 20
处理命令:
bash
sed -r 's#^([^[:space:]]+)[[:space:]]+([0-9]+)$#\1 \2kg#' fruit.txt
结果:
text
apple 10kg
banana 5kg
orange 20kg
这条命令同时完成了两件事:
- 使用捕获组保留水果名和数字;
- 重新输出时统一为一个空格,并在数字后添加
kg。
7. 删除每行指定位置的字符
删除每行第一个字符:
bash
sed -r 's#^.##' passwd
删除每行最后一个字符:
bash
sed -r 's#.$##' passwd
交换每行前两个字符:
bash
sed -r 's#^(.)(.)#\2\1#' passwd
这些示例适合练习捕获组和位置锚点。真实运维场景中,应先确认每一行的格式一致,否则很容易处理到不该处理的内容。
十、常见易错点
1. 忘记给 p 配合 -n
bash
sed '3p' test.txt
会让第 3 行显示两次。只打印第 3 行应写成:
bash
sed -n '3p' test.txt
2. 误以为不加 -i 也会修改文件
bash
sed 's/old/new/g' file
默认只把结果输出到终端。真正修改文件需要 -i,但更稳妥的方式是先预览,再使用:
bash
sed -i.bak 's/old/new/g' file
3. 忘记 g 只替换了第一处
bash
sed 's/old/new/' file
只替换每行第一次匹配。每行全部替换应写成:
bash
sed 's/old/new/g' file
4. 把正则地址范围当成数值范围
bash
sed -n '/3/,/6/p' file
它表示从"包含 3 的行"到"包含 6 的行",不是从数值 3 取到数值 6。
5. 分隔符和正文冲突
处理路径时直接使用 /,命令会出现大量转义:
bash
sed 's/\/usr\/local/\/opt\/app/g' file
换成 # 更清楚:
bash
sed 's#/usr/local#/opt/app#g' file
6. 使用扩展正则却没有加 -r 或 -E
bash
sed -n '/linux|apple/p' test.txt
在默认基础正则模式下,| 不一定会按"或者"处理。可以写成:
bash
sed -rn '/linux|apple/p' test.txt
7. .* 太贪婪
bash
sed -r 's#(.*) (.*)#\2 \1#' file
.* 会尽可能多地匹配内容。当一行里有多个空格或多个分隔符时,分组边界可能和预期不同。
如果字段由空白分隔,优先写成:
bash
sed -r 's#^([^[:space:]]+)[[:space:]]+([^[:space:]]+)$#\2 \1#' file
8. 直接对系统配置使用 -i
sed -i 执行成功,只能说明文本被改了,不能说明配置语法正确,也不能说明服务一定能正常启动。
更安全的流程是:
- 复制测试或直接预览;
- 使用
-i.bak留备份; - 使用
diff -u检查变化; - 执行对应程序的配置语法检查;
- 再重载服务并观察状态和日志。
9. 忽略不同系统的语法差异
以下写法带有明显的 GNU sed 特征:
-r;1~2;\U、\L大小写转换。
如果脚本要在 macOS、BSD 或其他 Unix 系统运行,需要提前测试兼容性,必要时改用 -E 或更通用的写法。
十一、sed 常用功能速查
1. 地址设置
| 地址格式 | 含义 |
|---|---|
n |
第 n 行 |
n,m |
第 n~m 行 |
n;m |
分别执行第 n 行和第 m 行对应的命令 |
1~2 |
奇数行,GNU sed 写法 |
2~2 |
偶数行,GNU sed 写法 |
$ |
最后一行 |
/正则/ |
正则匹配的行 |
/正则1/,/正则2/ |
从正则 1 匹配行到正则 2 匹配行 |
2. 常用操作
| 需求 | 命令示例 |
|---|---|
| 打印第 3 行 | sed -n '3p' file |
| 打印第 2~5 行 | sed -n '2,5p' file |
| 打印第 2 行和第 5 行 | sed -n '2p;5p' file |
| 打印最后一行 | sed -n '$p' file |
| 打印正则匹配行 | sed -n '/正则/p' file |
| 删除第 3 行 | sed '3d' file |
| 删除空行 | sed '/^$/d' file |
| 替换每行第一处 | sed 's#旧值#新值#' file |
| 替换每行所有匹配 | sed 's#旧值#新值#g' file |
| 只替换第 n 行 | sed 'ns#旧值#新值#g' file |
| 只替换匹配行 | sed '/正则/s#旧值#新值#g' file |
| 每行行首添加内容 | sed 's#^#新值#' file |
| 每行行尾添加内容 | sed 's#$#新值#' file |
| 在目标行后追加 | sed '/正则/a 内容' file |
| 在目标行前插入 | sed '/正则/i 内容' file |
| 替换目标整行 | sed '/正则/c 内容' file |
| 按字符一对一转换 | sed 'y/abc/ABC/' file |
| 修改前创建备份 | sed -i.bak 's#旧值#新值#g' file |
3. 模式空间与保持空间
| 命令 | 方向与作用 |
|---|---|
h |
模式空间覆盖保持空间 |
H |
模式空间追加到保持空间 |
g |
保持空间覆盖模式空间 |
G |
保持空间追加到模式空间 |
d |
删除模式空间并开始下一轮 |
N |
把下一行追加到模式空间 |
4. 操作思路总结
面对一条 sed 命令,可以按下面的顺序拆解:
- 看选项 :有没有
-n、-r、-i; - 看地址:处理所有行、指定行,还是正则匹配行;
- 看操作:打印、删除、替换,还是追加内容;
- 看标志 :替换命令有没有
g; - 看落盘方式 :只是输出,还是使用
-i修改文件; - 看安全措施:是否备份、对比并执行配置检查。
最后记住一句话:
sed先通过地址选中行,再在模式空间里执行操作;默认只输出结果,只有明确写入时才会改变文件。