sed 是 Linux 下处理文本的「瑞士军刀」,而 GNU sed是其主流版本,作为流编辑器它仅对输入做一次遍历,效率极高,尤其适合管道过滤、批量修改配置、处理日志等场景,是 Shell 脚本自动化的必备工具。
一、初识sed:流编辑器的核心本质
1. 什么是GNU sed
GNU sed 是Stream Editor(流编辑器)的实现版本,用于对输入流(文件/管道/标准输入)执行基础文本转换,核心特点是边读、边处理、边输出,全程仅遍历一次输入,处理大文件时速度极快且不占满内存。
2. sed的核心优势
- 无交互:无需打开编辑器,命令行直接执行,适配脚本自动化;
- 管道友好:可与grep、awk、ls等命令配合,组成文本处理流;
- 原地编辑:支持直接修改文件,还能生成备份,避免误操作;
- 功能强大:覆盖替换、增删改、行过滤、多行处理、字符转译等;
- 效率极高:一次遍历输入,GNU版本无行长度限制,适配海量文本。
3. 核心适用场景
小文本手动编辑用vim,批量文本处理、脚本自动化、管道过滤用sed,典型场景:
- 配置文件的批量注释/修改/替换;
- 日志文件的过滤、提取、格式化;
- 文本的行增删、去重、大小写转换;
- 管道中配合其他命令做文本过滤。
二、sed基础:调用方式与核心选项
1. 基本调用语法
GNU sed 有两种核心调用方式,日常轻量需求用直接执行命令,复杂逻辑用读取脚本文件,未指定输入文件或输入为-时,处理标准输入:
# 方式1:直接执行命令(最常用)
sed [核心选项] '处理脚本' 输入文件1 输入文件2...
# 方式2:从文件读取脚本(适合多命令复杂逻辑)
sed [核心选项] -f 脚本文件 输入文件1 输入文件2...
输出规则:默认打印到标准输出(仅预览结果,不修改原文件),可通过-i实现原地编辑,-n抑制默认输出。
2. 必学核心命令行选项
这是sed的「高频搭档」,区分基础必用和进阶实用,优先掌握前者:
| 选项 | 全称/补充 | 核心作用 | 实用场景 |
|------|-----------|----------|----------|
| -n | --quiet/--silent | 关闭默认输出,仅打印显式指定内容 | 精准打印匹配行,避免重复输出 |
| -i[后缀] | --in-place[=后缀] | 原地编辑文件,指定后缀生成备份 | 真正修改文件,-i.bak生成.bak备份,安全无风险 |
| -E | -r/--regexp-extended | 使用扩展正则表达式,无需转义+/?|() | 简化正则写法,日常推荐使用 |
| -e | --expression=脚本 | 指定执行的sed脚本,多脚本可多次使用 | 单命令行执行多个处理逻辑 |
| -f | --file=脚本文件 | 从文件读取执行脚本 | 复杂逻辑,将命令写入脚本文件 |
| --debug | - | 调试模式,打印执行过程 | 排查复杂sed脚本的执行问题 |
| -s | --separate | 将多个输入文件视为独立文件,非连续流 | 批量处理多文件时,避免跨文件匹配 |
| -z | --null-data | 以空字符(NUL)为行分隔符 | 适配find -print0等场景,处理含空格的文件名 |
| --sandbox | - | 沙箱模式,禁止e/w/r外部调用 | 执行未知sed脚本,防止安全风险 |
| --version/--help | - | 查看GNU sed版本/帮助信息 | 验证版本、查询选项/命令用法 |
| -b/--binary | - | 二进制模式处理 | 适配不同系统换行符(\n/\r\n),避免\r被忽略 |
3. sed的退出状态
执行后可通过echo $?查看退出码,用于脚本中判断执行结果:
- 0:执行成功;
- 1:命令/语法/正则无效,或使用
--posix的GNU扩展; - 2:部分输入文件无法打开,其余文件正常处理;
- 4:运行时I/O错误,直接中止执行;
- 自定义退出码:通过
q/Q指定,如sed 'Q42' 文件名,退出码为42。
三、sed核心逻辑:脚本结构与地址定位
sed的所有操作都是「针对指定行执行指定命令」,这是核心逻辑,而地址定位就是告诉sed「处理哪一行/哪些行」,脚本结构则规范了命令的执行格式。
1. sed脚本基础结构
sed脚本由一个或多个命令组成,核心格式为:[地址]命令[选项]
- 地址:可选,无地址则对所有行生效;可加
!取反,对不匹配的行执行命令; - 命令:单个字母的sed核心命令(如s/d/p/a),是具体的处理动作;
- 分隔方式:多个命令可用分号
;、换行分隔,或用-e指定多个脚本。
2. 地址定位规则:3种核心形式+取反
地址定位支持行号、正则、范围三种形式,可单独使用也可组合,加!实现「取反处理」,是sed的核心基础,必须吃透:
(1)数字地址:精准定位指定行
基于行号的硬定位,简单粗暴,GNU sed提供扩展语法适配批量行:
5:仅处理第5行;$:仅处理最后一行;1~2:奇数行(GNU扩展),~后为步长;0~2:偶数行(GNU扩展);5,+3:第5行及后续3行(共4行)。
(2)正则地址:匹配内容定位行
用正则表达式模糊筛选行,是最常用的定位方式,GNU sed支持多种扩展语法,避免转义和适配不同场景:
/正则/:匹配正则表达式的行(默认基本正则BRE,+/?|()需转义);\%正则%:用%作为分隔符,避免内容中/的转义(如匹配路径时);/正则/I:忽略大小写匹配(GNU扩展);/正则/M:多行模式,^/$可匹配换行符前后(GNU扩展)。
注意:加-E选项后使用扩展正则ERE,+/?|()无需转义,推荐日常使用。
(3)范围地址:定位连续多行
将「行号」和「正则」组合,用逗号,分隔,指定处理的行范围,适配批量连续行处理:
4,17:第4行到第17行;/start/,/end/:从匹配start的行到匹配end的行;2,/sed/:从第2行到第一个匹配sed的行;0,/regex/:从首行到第一个匹配regex的行(GNU扩展)。
(4)取反定位:处理不匹配的行
在任意地址后加!,表示对不匹配该地址的行执行命令,适合「排除式处理」:
/sed/!p:打印所有不含sed的行;3,5!d:删除除了第3-5行的所有行。
3. 地址定位实战示例
创建测试文件,后续所有基础示例均基于此,先执行创建:
cat > test.txt << EOF
1. Hello World
2. sed is a stream editor
3. learn sed, use sed
4. hello sed
5. test text
EOF
基于测试文件的地址定位示例,直观理解用法:
# 行号定位:处理第3行
sed '3s/sed/SED/' test.txt
# 正则定位:处理含hello的行(忽略大小写)
sed -E '/hello/I s/hello/HELLO/' test.txt
# 范围定位:处理第2行到第4行
sed '2,4s/.*/\U&/' test.txt
# 取反定位:处理不含sed的行,行尾追加[no sed]
sed '/sed/!s/$/ [no sed]/' test.txt
四、封神命令:s替换——sed的核心功能(90%日常使用)
sed的命令有很多,但90%的日常需求都围绕s替换命令,掌握它就掌握了sed的半壁江山,GNU sed对s命令提供了丰富的扩展,让文本替换更灵活、精准。
1. s替换基础语法
[地址]s/匹配内容/替换内容/标记
核心技巧:分隔符/可替换为任意单个字符(如#、%、@),当匹配/替换内容中包含/(如路径)时,用其他分隔符可避免大量反斜杠转义,这是日常必用技巧:
# 替换路径,用/做分隔符需转义,繁琐
sed 's/\/home\/test/\/data\/test/' 文件名
# 用#做分隔符,无需转义,简洁
sed 's#/home/test#/data/test#' 文件名
2. s替换核心标记:精准控制替换行为
标记加在最后一个分隔符后,可单个使用或组合使用(如gp、Ig),是s命令的「灵魂」,GNU sed的扩展标记大幅提升实用性:
| 标记 | 类型 | 核心作用 | 实用示例 |
|------|------|----------|----------|
| g | 基础 | 全局替换,默认仅替换每行第一个匹配项 | s/sed/SED/g:每行所有sed都替换为SED |
| 数字(1/2/3...) | 基础 | 仅替换每行第N个匹配项 | s/sed/SED/2:仅替换每行第二个sed |
| p | 基础 | 替换成功后打印该行,需配合-n | sed -n 's/sed/SED/gp':仅打印替换成功的行 |
| w 文件名 | 基础 | 替换成功后将内容写入指定文件 | s/sed/SED/gw result.txt:替换结果写入result.txt |
| I/i | GNU扩展 | 忽略大小写匹配 | s/hello/你好/I:匹配所有hello/Hello/HELLO |
| M/m | GNU扩展 | 多行模式,^/$匹配换行符前后 | 配合N命令处理跨行匹配 |
| e | GNU扩展 | 将替换结果作为shell命令执行 | 适合动态生成并执行命令 |
3. 替换内容特殊符号:实现高级替换
在「替换内容」中可使用特殊符号,实现引用匹配内容、大小写转换等高级操作,无需重复编写匹配规则,是提升效率的关键:
(1)基础引用符号
&:引用匹配到的整个文本,适合给匹配内容加前缀/后缀;\N:引用正则中第N个\( \)分组的内容(-E扩展正则中无需转义()),适合调整内容顺序、提取指定部分。
示例:
# &:给所有sed加中括号
sed 's/sed/[&]/g' test.txt
# \N:交换Hello和World(-E扩展正则,无需转义())
echo "Hello World" | sed -E 's/(\w+) (\w+)/\2 \1/'
# 输出:World Hello
(2)大小写转换符号
用于替换内容的格式化,\L/\U持续生效,直到\E结束,\l/\u仅对下一个字符生效,GNU sed专属扩展,非常实用:
\U:后续内容全部转为大写,\E结束;\u:仅下一个字符转为大写;\L:后续内容全部转为小写,\E结束;\l:仅下一个字符转为小写;\E:结束大小写转换。
示例:
echo "hello world" | sed 's/.*/\U&/' # 全部大写:HELLO WORLD
echo "HELLO WORLD" | sed 's/.*/\L&/' # 全部小写:hello world
echo "hello world" | sed 's/\w\+/\u&/g' # 首字母大写:Hello World
echo "Hello World" | sed -E 's/(\w+) (\w+)/\U\1\E \L\2/' # HELLO world
4. s替换高频实战示例
# 1. 全局替换,忽略大小写,仅打印替换行
sed -n 's/sed/SED/Igp' test.txt
# 2. 仅替换第2-4行的第一个hello为HELLO
sed '2,4s/hello/HELLO/' test.txt
# 3. 替换路径并将结果写入文件
sed 's#/var/log#/data/log#gw log_path.txt' app.conf
# 4. 提取日志中的IP(假设日志格式:2025-01-01 192.168.1.1 GET /)
echo "2025-01-01 192.168.1.1 GET /" | sed -E 's/.*(([0-9]{1,3}\.){3}[0-9]{1,3}).*/\1/'
五、sed常用基础命令:速查+实战
除了s替换命令,sed还有一系列单个字母的基础命令,配合地址定位可实现增删改查、行打印、行号显示等功能,简单易记,是处理文本的基础工具集,以下为核心常用命令,附速查和实战示例(均基于test.txt,加-i可原地修改文件):
| 命令 | 核心作用 | 基础语法 | 实战示例 |
|---|---|---|---|
d |
删除匹配行 | [地址]d |
/^$/d:删除空行;/sed/d:删除含sed的行 |
p |
打印匹配行 | [地址]p |
-n '2,4p':仅打印2-4行;-n '/hello/Ip':忽略大小写打印含hello的行 |
a 文本 |
行后追加文本 | [地址]a 文本 |
3a 6. add new line:第3行后追加指定内容;/sed/a >>>:含sed行后加>>> |
i 文本 |
行前插入文本 | [地址]i 文本 |
5i >>> test <<<:第5行前插入内容;$i last line before:最后一行前插入 |
c 文本 |
替换匹配行为指定文本 | [地址]c 文本 |
5c 5. replace text:将第5行替换为指定内容;/test/c test replace:替换含test的行 |
= |
打印行号 | [地址]= |
-n '/sed/=':仅打印含sed的行号;-n '=;p':打印所有行的行号+内容 |
y/源/目标/ |
字符一一转译 | [地址]y/源字符/目标字符/ |
y/12345/abcde/:1→a、2→b…5→e;y/abc/ABC/:小写abc转大写ABC |
n |
读取下一行到模式空间 | [地址]n |
n;d:删除偶数行;1~2n;d:删除奇数行 |
关键注意:
p命令必须配合-n使用,否则会重复打印内容;y命令的源字符和目标字符长度必须一致,仅支持单个字符的一一替换;- 所有命令不加地址时,对所有行生效,使用
d命令时需谨慎。
六、sed工作原理:双缓冲区机制(基础→进阶)
如果仅做单行文本处理,掌握以上内容已足够;若要处理多行合并、文本反转、相邻行去重等复杂场景,需理解sed的双缓冲区机制——GNU sed通过两个缓冲区实现数据的临时存储和处理,这是所有进阶用法的核心。
1. 两个核心缓冲区
sed全程基于行循环处理文本,维护两个缓冲区,职责明确:
| 缓冲区 | 中文名 | 核心特性 | 作用 |
|--------|--------|----------|------|
| Pattern Space | 模式空间 | 临时缓冲区,行循环结束后自动清空 | 存储当前读取的行,所有基础命令(s/d/p等)默认操作此空间 |
| Hold Space | 保持空间 | 持久缓冲区,不会自动清空,仅手动操作 | 暂存数据,用于跨行列的数据传递和处理,相当于「临时仓库」 |
2. sed基本处理循环
即使是进阶用法,也基于此基础循环,只是对缓冲区的操作更复杂:
- 从输入读取一行,去除末尾换行符,写入模式空间;
- 按sed脚本的顺序,对匹配地址的行执行所有命令(操作模式空间);
- 若未指定
-n,打印模式空间内容,恢复末尾换行符; - 清空模式空间,开始下一次循环,直到输入结束。
3. 操作双缓冲区的进阶命令
通过以下命令可实现两个缓冲区的复制、追加、交换,以及多行处理,这些命令是进阶用法的关键,无需死记硬背,理解作用即可,需要时直接套用示例:
(1)缓冲区互操作命令
| 命令 | 核心作用 |
|---|---|
h |
将模式空间内容覆盖写入保持空间 |
H |
将模式空间内容追加到保持空间(追加前加换行符) |
g |
将保持空间内容覆盖写入模式空间 |
G |
将保持空间内容追加到模式空间(追加前加换行符) |
x |
交换模式空间和保持空间的内容 |
(2)多行处理命令
| 命令 | 核心作用 |
|---|---|
N |
读取下一行,追加到模式空间(加换行符),实现多行加载 |
D |
删除模式空间中第一个换行符前的内容,不重启循环 |
P |
打印模式空间中第一个换行符前的内容 |
七、sed进阶用法:多行处理+流程控制
基于双缓冲区机制,GNU sed支持多行文本处理和简单流程控制,适用于复杂文本场景,以下为核心场景和实现方式,附可直接套用的示例。
1. 多行文本处理:经典场景与实现
sed默认按单行处理,通过N/D/P配合缓冲区命令,可将多行加载到模式空间统一处理,最经典的场景为合并跨行文本。
示例:处理行尾以=结尾的软换行文本,合并为单行
# 1. 创建测试文件
cat > soft_line.txt << EOF
这是一=
行跨行了的=
文本,需要=
合并为单行
EOF
# 2. 合并命令
sed ':x ; /=$/ { N ; s/=\n//g ; bx }' soft_line.txt
# 输出:这是一行跨行了的文本,需要合并为单行
命令解释::x定义分支标签,/=$/匹配行尾带=的行,N读取下一行到模式空间,s/=\n//g删除软换行,bx跳回标签x继续循环,直到无匹配行。
2. 分支与流程控制:实现复杂逻辑
GNU sed支持通过标签+分支命令实现简单的流程控制,适用于多条件的复杂脚本,核心命令为标签定义和分支跳转,可配合s命令实现「条件替换」:
:标签:定义分支标签,无实际操作,仅作为跳转标记;b 标签:无条件跳转到指定标签,无标签则重启行循环;t 标签:有条件跳转,仅当本次循环中s命令替换成功时,跳转到指定标签;T 标签:有条件跳转,仅当本次循环中s命令未替换成功时,跳转到指定标签。
核心特点:t/T与s命令强绑定,适合实现「匹配替换后继续处理」的逻辑。
八、sed高频实战技巧:一行命令搞定日常需求
融合GNU sed官方推荐用法和实际工作场景,分基础实用和进阶实战,覆盖配置文件处理、日志分析、文本格式化、批量操作等,直接抄作业即可。
1. 基础实用技巧(80%日常场景)
# 1. 打印文件第5-10行(替代head/tail)
sed -n '5,10p' 文件名
# 2. 删除空行和以#开头的注释行(处理配置/日志)
sed '/^$/d; /^#/d' 文件名
# 3. 行首/行尾去空格/制表符(文本格式化)
sed 's/^[ \t]*//' # 行首去空格
sed 's/[ \t]*$//' # 行尾去空格
sed 's/^[ \t]*//; s/[ \t]*$//' # 同时去首尾空格
# 4. 批量注释/取消注释配置文件行
sed '3,8s/^/#/' 配置文件.conf # 注释3-8行
sed '/listen/s/^#//' 配置文件.conf # 仅取消含listen的行的注释
# 5. 原地替换并生成备份(安全修改文件)
sed -i.bak 's/abc/123/g' 文件名 # 替换后生成文件名.bak备份
# 6. 统计文件行数(替代wc -l)
sed -n '$=' 文件名
# 7. 所有行尾加分号(编程配置格式化)
sed 's/$/;/' 文件名
# 8. 给文件所有行加行号
sed = 文件名 | sed 'N;s/\n/ /'
2. 进阶实战技巧(复杂场景)
# 1. 文本去重(仅相邻行,替代uniq)
sed '$!N; /^\(.*\)\n\1$/!P; D' 文件名
# 2. 给文件首行添加版权头(GNU扩展0,首行前添加)
sed -i '0r copyright.txt' *.c # 从文件读取版权头内容
sed -i '1i/* Copyright (C) 2025 */' *.c # 直接添加文本
# 3. 反转文件行顺序(替代tac,基于双缓冲区)
sed -n '1!G; $p; h' 文件名
# 4. 批量重命名文件(配合ls/sh,将.txt改为.md)
ls *.txt | sed 's/\(.*\)\.txt/mv & \1.md/' | sh
# 5. 提取配置文件中的键值对(如key=value,仅打印value)
sed -E 's/^[a-zA-Z0-9_]+=(.*)/\1/' config.conf
# 6. 删除文件中最后一行
sed '$d' 文件名
# 7. 将文件中所有字母转为小写
sed 's/.*/\L&/' 文件名