// Linux · 2025-01-23

Linux文本处理三剑客之 sed 流编辑器:从入门到精通[⭐建议收藏!⭐]

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 为 GNU sed 旧版本兼容)。
  • -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