Linux文本处理三剑客之sed详解

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,最重要的不是先背命令,而是先弄清它的处理流程:

  1. 从输入中读取一行;
  2. 把这一行放入模式空间;
  3. 判断这一行是否符合地址条件;
  4. 如果符合,就执行 psda 等操作;
  5. 默认把处理后的模式空间输出;
  6. 清空模式空间,再读取下一行。

这里的"地址"不是 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
打印包含 linuxapple 的行
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

为什么会多出 1316

因为 /3/ 的意思不是数字 3,而是"包含字符 3 的行";/6/ 也不是数字 6,而是"包含字符 6 的行"。因此:

  • 第一个范围从 3 开始,到 6 结束;
  • 后面 13 又匹配了 /3/,于是开启第二个范围;
  • 直到 16 匹配 /6/,第二个范围才结束。

如果就是要匹配内容只有 36 的行,可以把边界写完整:

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:2112: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

cs 很容易混淆:

  • 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 当前正在处理的"工作台"。

默认流程是:

  1. 读取一行到模式空间;
  2. 在模式空间中执行 sydp 等操作;
  3. 默认输出模式空间;
  4. 开始下一轮,读取下一行。

前面所有行内替换,本质上都是在模式空间中完成的。

2. 保持空间

保持空间可以理解为一个额外的"临时剪贴板"。

它不会像模式空间一样自动装入当前行,而是由 hHgG 等命令主动存取。保持空间初始为空,可以跨处理周期保存内容。

3. 常用命令

命令 作用
h 用模式空间覆盖保持空间
H 把模式空间追加到保持空间
g 用保持空间覆盖模式空间
G 把保持空间追加到模式空间
d 删除模式空间,并立即开始下一轮
N 读取下一行,追加到当前模式空间,中间保留换行符

可以用大小写来帮助记忆:

  • 小写 hg:覆盖;
  • 大写 HG:追加。

其中:

  • 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

第一条命令的逻辑是:

  1. 第一行不执行 G,直接通过 h 存入保持空间;
  2. 从第二行开始,使用 G 把之前保存的内容追加到当前行后面;
  3. 使用 h 把新的整体再存回保持空间;
  4. $!d 表示只要不是最后一行,就删除模式空间并进入下一轮,不输出;
  5. 到最后一行时不执行 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 执行成功,只能说明文本被改了,不能说明配置语法正确,也不能说明服务一定能正常启动。

更安全的流程是:

  1. 复制测试或直接预览;
  2. 使用 -i.bak 留备份;
  3. 使用 diff -u 检查变化;
  4. 执行对应程序的配置语法检查;
  5. 再重载服务并观察状态和日志。

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 命令,可以按下面的顺序拆解:

  1. 看选项 :有没有 -n-r-i
  2. 看地址:处理所有行、指定行,还是正则匹配行;
  3. 看操作:打印、删除、替换,还是追加内容;
  4. 看标志 :替换命令有没有 g
  5. 看落盘方式 :只是输出,还是使用 -i 修改文件;
  6. 看安全措施:是否备份、对比并执行配置检查。

最后记住一句话:

sed 先通过地址选中行,再在模式空间里执行操作;默认只输出结果,只有明确写入时才会改变文件。

相关推荐
dok121 小时前
Johannes 《Linux内核模块与设备驱动开发:编写Linux驱动程序》 (9)manual_cdev
linux·运维·驱动开发
czhaii1 小时前
汇川注塑机伺服驱动器故障维修排除方法
运维·服务器
Cx330❀1 小时前
【Linux网络】深入 HTTP 协议(五):从 Cookie/Session 原理到 C++ 源码实战
linux·运维·服务器·开发语言·网络·c++·http
Dobby_051 小时前
【CICD】Jenkins Pipeline 快速学习入门
运维·学习·jenkins
神秘的MT2 小时前
C# WinForm Socket 类 常用方法 (C# .NET,TCP 场景)
服务器·网络·学习·tcp/ip·c#·winform
小小、码农3 小时前
Linux进程概念
linux·服务器·网络
nxb55611 小时前
haproxy算法,ip透传,自定义haproxy错误页面
运维·服务器
Nebula嵌入式11 小时前
【C语言】09-深入解析main函数
linux·c语言·开发语言·嵌入式
AKAMAI12 小时前
你的源服务器可能是你做出的最昂贵决定
运维·人工智能·云计算