1. 引言
sed(流编辑器,Stream Editor)是 Unix/Linux 系统中一款经典而强大的文本处理工具。它以逐行读取的方式操作文件,在内存中的“模式空间”(Pattern Space)中处理文本,支持替换、删除、插入、打印等多种操作。作为非交互式编辑器,sed 的设计初衷是为批量处理和自动化脚本提供高效支持。结合正则表达式,sed 能够实现复杂的文本模式匹配和转换。
与其他工具(如 awk 或 grep)相比,sed 的独特优势在于其流式处理能力——无需将整个文件加载到内存即可完成编辑,特别适合处理大文件或实时数据流。本文将从基础语法入手,逐步深入到高级功能,并通过丰富示例展示 sed 在实际场景中的应用。
2. sed 语法格式
sed 的基本语法如下:
sed [选项] '命令' 文件名
- 选项:控制
sed的行为,例如是否直接修改文件。 - 命令:指定具体的编辑操作,通常由地址范围和操作指令组成。
- 文件名:待处理的文件,可以是单个文件、多个文件,或通过管道从标准输入接收数据。
2.1 常用选项
以下是 sed 最常用的选项及其功能:
-n:禁止默认输出,仅显示由p命令明确指定的行。-i[后缀]:直接修改源文件内容(若指定后缀,会备份原文件,如-i.bak)。-e 命令:允许多个独立命令依次执行,适合复杂编辑任务。-E或-r:启用扩展正则表达式(ERE),简化正则语法(-E为现代标准,-r为 GNUsed旧版本兼容)。-f 脚本文件:从外部脚本文件加载命令,适合批量处理。
示例:
sed -n '1,5p' file.txt # 仅打印第 1 到 5 行
sed -i 's/old/new/g' file.txt # 直接将文件中所有 "old" 替换为 "new"
sed -e '1d' -e '2s/a/b/' file.txt # 删除第 1 行并将第 2 行的 "a" 替换为 "b"
2.2 地址范围语法
sed 的命令通常由地址范围和操作组成,地址用于指定处理的行范围。格式为:
sed '起始地址,结束地址 命令' 文件名
- 起始地址 和 结束地址 可以是行号、正则表达式或特殊符号(如
$表示最后一行)。 - 若省略地址,则命令作用于所有行。
示例:
sed '2,5d' file.txt # 删除第 2 到 5 行
sed '/start/,/end/s/a/b/' file.txt # 在 "start" 到 "end" 之间的行中将 "a" 替换为 "b"
sed '3,$p' file.txt # 从第 3 行到最后一行打印
3. sed 核心命令详解
以下是 sed 的核心命令及其用法,每个命令均配有详细语法和示例。
3.1 替换命令 s
语法:
sed '地址 s/模式/替换内容/[标志]' 文件名
s/pattern/replacement/:替换每行中第一个匹配的模式。- 标志:
g:全局替换(每行所有匹配项)。p:打印替换后的行(需配合-n)。- 数字(如
2):仅替换第 N 次匹配。
示例:
sed 's/book/books/' file.txt # 将每行第一个 "book" 替换为 "books"
sed 's/book/books/g' file.txt # 全局替换所有 "book" 为 "books"
sed -n 's/book/books/p' file.txt # 仅打印替换后的行
sed '2,4 s/book/books/2' file.txt # 在第 2 到 4 行中替换每行第 2 个 "book"
3.2 删除命令 d
语法:
sed '地址 d' 文件名
- 删除指定地址范围的行。
示例:
sed '2d' file.txt # 删除第 2 行
sed '2,5d' file.txt # 删除第 2 到 5 行
sed '/^$/d' file.txt # 删除所有空行
sed '/pattern/d' file.txt # 删除匹配 "pattern" 的行
3.3 插入命令 a 和 i
语法:
sed '地址 a\插入内容' 文件名
sed '地址 i\插入内容' 文件名
a:在匹配行之后插入文本。i:在匹配行之前插入文本。
示例:
sed '2a\New line after 2' file.txt # 在第 2 行后插入新行
sed '/test/i\Before test' file.txt # 在含 "test" 的行前插入
sed '1,3 a\Added text' file.txt # 在第 1 到 3 行后各插入一行
3.4 打印命令 p
语法:
sed -n '地址 p' 文件名
- 打印指定行,通常与
-n配合使用,避免默认输出所有行。
示例:
sed -n '1,5p' file.txt # 打印第 1 到 5 行
sed -n '/test/p' file.txt # 打印含 "test" 的行
sed -n 's/book/books/p' file.txt # 打印替换后的行
sed -n '100,$p' file.txt # 打印第 100 行到最后一行
提示:
$表示文件最后一行。
3.5 字符转换命令 y
语法:
sed '地址 y/源字符/目标字符/' 文件名
- 将源字符集中的每个字符逐一转换为目标字符集中的对应字符(一对一映射)。
示例:
sed 'y/abc/xyz/' file.txt # 将 "a" 转为 "x","b" 转为 "y","c" 转为 "z"
sed '1,3 y/123/ABC/' file.txt # 在第 1 到 3 行中将 "1" 转为 "A" 等
3.6 整行替换命令 c
语法:
sed '地址 c\新内容' 文件名
- 用指定文本替换整个匹配行。
示例:
sed '2c\New second line' file.txt # 将第 2 行替换为新内容
sed '/test/c\Replaced line' file.txt # 将含 "test" 的行替换
sed '1,3 c\New text' file.txt # 将第 1 到 3 行替换为一行
4. sed 中的正则表达式
sed 默认使用基本正则表达式(BRE),通过 -E 或 -r 选项可启用扩展正则表达式(ERE)。两者主要区别在于元字符的转义需求:
- BRE:需转义
+、?、|、()等。 - ERE:直接使用这些字符,无需转义。
4.1 正则表达式示例
BRE 示例:
sed 's/\(test\)\+/tests/' file.txt # 匹配 1 个或多个 "test",替换为 "tests"
ERE 示例:
sed -E 's/(test)+/tests/' file.txt # 同上,语法更简洁
sed -E 's/ab|cd/xy/' file.txt # 替换 "ab" 或 "cd" 为 "xy"
4.2 局限性
尽管支持正则表达式,sed 不支持某些高级功能(如前瞻后顾、预定义字符类 \d、\w)。若需这些功能,可转向 perl 或 awk。
5. 复杂操作实例
以下示例展示 sed 在实际场景中的强大功能,涵盖多点编辑、文件读写、字符串匹配等。
5.1 多点编辑:使用 -e
场景:对不连续的行执行不同操作。
示例:
sed -e '1,5d' -e '7s/test/check/' -e '10p' file.txt
1,5d:删除第 1 到 5 行。7s/test/check/:将第 7 行的 “test” 替换为 “check”。10p:打印第 10 行。
注意:逗号(,)仅限连续范围,-e 用于分离独立操作。
5.2 文件读写:w 和 r
语法:
sed '地址 w 输出文件' 输入文件
sed '地址 r 输入文件' 文件名
示例:
sed '1,10 w output.txt' file.txt # 将第 1 到 10 行写入 output.txt
sed '/test/r extra.txt' file.txt # 在含 "test" 的行后插入 extra.txt 内容
应用:日志筛选、内容合并。
5.3 字符串匹配与引用:\1 和 &
语法:
sed '地址 s/模式/替换内容/' 文件名
示例 1:捕获组 \1:
sed 's/\(love\)able/\1rs/' file.txt # "loveable" -> "lovers"
sed -E 's/(test)-[0-9]+/\1/' file.txt # "test-123" -> "test"
示例 2:整体匹配 &:
sed 's/test/&-modified/' file.txt # "test" -> "test-modified"
sed 's/[0-9]+/(&)/' file.txt # "123" -> "(123)"
5.4 行控制与打印
示例 1:修改下一行:
sed '/test/{n; s/aa/bb/;}' file.txt
- 匹配 “test” 后,跳到下一行 (
n),将 “aa” 替换为 “bb”。
示例 2:打印奇偶行:
sed -n '1,${p;n;}' file.txt # 奇数行
sed -n '1,${n;p;}' file.txt # 偶数行
{p;n;}:打印当前行,跳过下一行。
示例 3:仅打印第 N 行:
sed '5q;d' file.txt # 打印第 5 行后退出
5.5 提取复杂 HTML 标签
场景:从 HTML 文件中提取嵌套的 <script> 标签及其内容。
示例:
# input.html 内容
<html>
<head>
<script type="text/javascript">
function hello() {
alert('Hello, World!');
}
</script>
</head>
<body>
<script src="app.js"></script>
</body>
</html>
# 提取所有 <script> 标签及其内容
sed -n '/<script.*>/,/<\/script>/p' input.html
输出:
<script type="text/javascript">
function hello() {
alert('Hello, World!');
}
</script>
<script src="app.js"></script>
说明:此命令通过正则表达式匹配 <script> 标签的开始和结束,提取包含多行内容的脚本块,适用于解析 HTML 或其他标记语言文件。
6. 高级功能与实战
6.1 标签与分支:b 和 t
语法:
sed '地址 b 标签名' 文件名
sed '地址 t 标签名' 文件名
示例:
sed '/skip/b end; s/a/b/; :end' file.txt # 匹配 "skip" 的行跳到 :end
sed 's/test/new/t next; d; :next' file.txt # 替换成功后跳到 :next,否则删除
应用:条件逻辑处理,如跳过特定行或根据替换结果执行不同操作。
6.2 保持与交换:h 和 x
语法:
sed '地址 h' 文件名
sed '地址 x' 文件名
示例:
sed -e '/test/h' -e '$G' file.txt # 将含 "test" 的行追加到末尾
sed '/swap/x' file.txt # 交换模式空间与保持缓冲区内容
应用:行复制、内容重排、临时存储数据。
6.3 脚本化操作
脚本文件 script.sed:
# 注释:删除空行并替换
/^$/d
s/test/check/g
执行:
sed -f script.sed file.txt
应用:批量处理多个文件,简化复杂操作。
6.4 高级正则表达式应用
场景:清理日志文件中特定格式的错误信息。
示例:
# 日志文件 error.log 内容
2025-06-01 12:00:00 ERROR: timeout
2025-06-01 12:01:00 INFO: success
2025-06-01 12:02:00 ERROR: connection failed
# 删除所有含 "ERROR" 的行
sed '/ERROR/d' error.log
输出:
2025-06-01 12:01:00 INFO: success
场景扩展:仅保留错误信息的日期和时间。
示例:
sed -n '/ERROR/s/^\([0-9-]\+ [0-9:]\+\).*/\1/p' error.log
输出:
2025-06-01 12:00:00
2025-06-01 12:02:00
说明:使用捕获组提取日期和时间,-n 和 p 确保仅输出匹配结果。
6.5 处理 CSV 文件
场景:从 CSV 文件中提取特定列或替换值。
示例:
# data.csv 内容
name,age,city
Alice,25,New York
Bob,30,London
# 提取第二列(年龄)
sed 's/[^,]*,\([^,]*\),.*/\1/' data.csv
输出:
age
25
30
说明:使用正则表达式匹配并提取 CSV 文件的第二列。
7. 性能优化与最佳实践
7.1 性能优化
- 提前退出:使用
q命令在处理完必要行后退出,减少不必要操作。sed '10q' file.txt # 仅处理前 10 行 - 限制范围:通过地址范围(如
1,100)缩小处理范围,避免全文件扫描。 - 流式处理:利用
sed的流式特性,处理大文件时避免加载整个文件到内存。
7.2 最佳实践
- 测试优先:在正式操作前,使用
echo或临时文件测试命令,避免直接用-i修改原文件导致数据丢失。echo "test content" | sed 's/test/check/' - 正则优化:启用
-E简化正则表达式书写,减少转义字符的使用。 - 备份文件:使用
-i.bak创建备份,防止意外修改。 - 模块化脚本:将复杂操作写入脚本文件(如
script.sed),提高可维护性。
7.3 扩展工具
若 sed 功能不足,可搭配以下工具:
- awk:适合复杂字段处理和数据分析。
- perl:支持更高级的正则表达式和复杂逻辑。
- tr:专注于字符级转换。
8. 实战案例:综合应用
8.1 日志分析
场景:从服务器日志中提取特定时间段的错误信息并格式化输出。
示例:
# server.log 内容
2025-06-01 10:00:00 INFO: Server started
2025-06-01 10:01:00 ERROR: Connection timeout
2025-06-01 10:02:00 ERROR: Disk full
# 提取 10:01:00 后的错误日志并格式化
sed -n '/2025-06-01 10:01:00/,/2025-06-01 10:02:00/s/ERROR: \(.*\)/Error: \1/p' server.log
输出:
Error: Connection timeout
Error: Disk full
说明:使用范围匹配和捕获组提取并格式化错误信息。
8.2 代码清理
场景:清理代码文件中的注释和空行。
示例:
# code.js 内容
// This is a comment
function hello() {
// Do something
console.log('Hello');
}
// Main function
function main() {
}
# 删除单行注释和空行
sed '/^\/\//d; /^$/d' code.js
输出:
function hello() {
console.log('Hello');
}
function main() {
}
说明:使用正则表达式匹配 // 开头的注释行和空行并删除。
8.3 批量文件处理
场景:对目录下所有 .txt 文件中的特定字符串进行替换。
示例:
for file in *.txt; do
sed -i 's/old/new/g' "$file"
done
说明:结合 shell 循环实现批量处理,-i 直接修改文件。
9. 总结
sed 作为 Unix/Linux 生态中的核心工具,以其高效的流式处理能力和灵活的正则表达式支持,广泛应用于文本处理、日志分析、代码清理等场景。通过掌握核心命令(如 s、d、p)、高级功能(如分支、保持缓冲区)和正则表达式,用户可以轻松应对从简单替换到复杂自动化任务的各种需求。
通过本文的学习,你将能够:
- 熟练使用
sed的基础命令和选项。 - 掌握正则表达式在
sed中的应用。 - 灵活运用高级功能处理复杂场景,如提取
<script>标签、日志分析等。 - 结合最佳实践优化
sed的性能和可靠性。
原文 https://blog.csdn.net/2301_79518550/article/details/145284324