sed 流编辑器完全指南
一、概述
什么是 sed?
sed(Stream Editor,流编辑器)是 Unix/Linux 系统中的非交互式文本编辑器,用于对输入流(文件或管道)进行基于文本的转换。它按行处理文本,是文本处理的瑞士军刀。
核心特点
-
非交互式:通过命令行参数或脚本执行,无需人工干预
-
流式处理:逐行读取、处理、输出,内存占用小
-
强大的正则表达式支持:支持基本和扩展正则表达式
-
脚本化:可编写 sed 脚本文件,实现复杂处理
-
高效快速:纯文本处理时性能优于大多数脚本语言
二、基本语法
bash
# 基本格式
sed [选项] '命令' 输入文件
sed [选项] -f 脚本文件 输入文件
# 管道使用
cat 文件 | sed [选项] '命令'
命令输出 | sed [选项] '命令'
三、常用选项详解
| 选项 | 说明 | 示例 |
|---|---|---|
-n |
静默模式,只输出处理过的行(常与 p 命令配合) |
sed -n '1,5p' file.txt |
-e |
指定多个编辑命令 | sed -e 's/a/A/' -e 's/b/B/' file.txt |
-f |
从脚本文件读取 sed 命令 | sed -f script.sed file.txt |
-i[扩展名] |
直接修改原文件(加扩展名则备份) | sed -i.bak 's/foo/bar/' file.txt |
-r (GNU) |
启用扩展正则表达式 | sed -r 's/(ab)+/X/g' file.txt |
-E (BSD/macOS) |
启用扩展正则表达式 | sed -E 's/(ab)+/X/g' file.txt |
--version |
显示版本信息 | sed --version |
--help |
显示帮助信息 | sed --help |
四、核心命令完全参考
1. 文本替换 (s 命令)
bash
# 基本语法
s/正则表达式/替换文本/标志
# 常用示例
sed 's/old/new/' file.txt # 每行第一个匹配替换
sed 's/old/new/g' file.txt # 全局替换(所有匹配)
sed 's/old/new/2' file.txt # 每行第2个匹配替换
sed 's/old/new/ig' file.txt # 忽略大小写并全局替换
# 标志说明
g - 全局替换(所有匹配)
i - 忽略大小写
p - 打印替换成功的行(常与 -n 合用)
w 文件 - 将替换成功的行写入文件
# 高级示例
sed -n 's/pattern/replacement/pw output.txt' file.txt
sed 's/pattern/replacement/2g' file.txt # 从第2个开始全局替换
2. 删除行 (d 命令)
bash
# 基本删除
sed '3d' file.txt # 删除第3行
sed '3,7d' file.txt # 删除3-7行
sed '3,+5d' file.txt # 从第3行开始删除6行
sed '$d' file.txt # 删除最后一行
sed '1~2d' file.txt # 删除奇数行(从第1行开始,每隔1行删除)
# 模式匹配删除
sed '/pattern/d' file.txt # 删除匹配行
sed '/^#/d' file.txt # 删除注释行(#开头)
sed '/^$/d' file.txt # 删除空行
sed '/^\s*$/d' file.txt # 删除空白行(空格/tab组成)
# 范围删除
sed '/start/,/end/d' file.txt # 删除从start到end的所有行
sed '10,/end/d' file.txt # 从第10行删除到匹配end的行
3. 打印 (p 命令)
bash
# 基本打印(常与 -n 选项配合)
sed -n '3p' file.txt # 只打印第3行
sed -n '3,7p' file.txt # 打印3-7行
sed -n '10,$p' file.txt # 从第10行打印到最后
sed -n '/pattern/p' file.txt # 打印匹配行
# 范围打印
sed -n '/start/,/end/p' file.txt # 打印start到end的所有行
sed -n '/pattern/,+3p' file.txt # 打印匹配行及其后3行
4. 插入/追加/替换行
bash
# 插入行 (i - 在指定行前插入)
sed '3i\插入的内容' file.txt # 在第3行前插入
sed '/pattern/i\插入的内容' file.txt # 在匹配行前插入
sed '3i第一行\n第二行' file.txt # 插入多行(使用\n分隔)
# 追加行 (a - 在指定行后追加)
sed '3a\追加的内容' file.txt # 在第3行后追加
sed '/pattern/a\追加的内容' file.txt # 在匹配行后追加
sed '$a\文件末尾追加' file.txt # 在文件末尾追加
# 替换行 (c - 替换整行)
sed '3c\替换为这行' file.txt # 替换第3行
sed '/pattern/c\新行内容' file.txt # 替换匹配行
sed '2,4c\替换为一行' file.txt # 将2-4行替换为一行
# 注意:BSD/macOS sed 需要额外的换行符
sed -i '' '3i\
插入的内容' file.txt
5. 文件读写操作
bash
# 读取文件 (r - 将文件内容插入)
sed '3r otherfile.txt' file.txt # 在第3行后插入文件内容
sed '/pattern/r otherfile.txt' file.txt # 在匹配行后插入
sed '$r otherfile.txt' file.txt # 在文件末尾追加文件内容
# 写入文件 (w - 将行写入文件)
sed -n '/pattern/w output.txt' file.txt # 将匹配行写入文件
sed '1,10w part1.txt' file.txt # 将1-10行写入文件
sed -n 'w output.txt' file.txt # 写入所有行(相当于cp)
# 示例:分割文件
sed -n '1,100w part1.txt' file.txt
sed -n '101,200w part2.txt' file.txt
6. 字符转换 (y 命令)
bash
# 语法:y/源字符集/目标字符集/
sed 'y/abc/ABC/' file.txt # a→A, b→B, c→C(一一对应)
sed 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' file.txt # 转大写
sed 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/' file.txt # 转小写
# 注意:y命令处理的是字符,不是字符串
7. 多命令执行
bash
# 方法1:使用 -e 选项
sed -e 's/foo/bar/' -e '/baz/d' file.txt
# 方法2:使用分号分隔
sed 's/foo/bar/; /baz/d' file.txt
# 方法3:多行命令(使用反斜杠续行)
sed '
s/foo/bar/
s/baz/qux/
/pattern/d
' file.txt
# 方法4:使用脚本文件
echo "s/foo/bar/" > script.sed
echo "/baz/d" >> script.sed
sed -f script.sed file.txt
五、高级功能与技巧
1. 模式空间与保持空间
bash
# 保持空间相关命令
h # 将模式空间复制到保持空间(覆盖)
H # 将模式空间追加到保持空间
g # 将保持空间复制到模式空间(覆盖)
G # 将保持空间追加到模式空间
x # 交换模式空间和保持空间
# 示例:反转文件行顺序
sed -n '1!G; h; $p' file.txt
# 示例:合并相邻行
sed 'N; s/\n/ /' file.txt # 将两行合并为一行
2. 条件分支与跳转
bash
# 定义标签和跳转
:label_name # 定义标签
b label_name # 无条件跳转到标签
t label_name # 如果发生替换则跳转
T label_name # 如果没发生替换则跳转
# 示例:跳过特定行的处理
sed '/^#/b; s/foo/bar/' file.txt # 注释行不执行替换
# 示例:循环处理
sed ':a; s/foo/bar/; ta' file.txt # 重复替换直到没有更多匹配
3. 行号处理
bash
# 显示行号
sed '=' file.txt # 每行前显示行号
sed -n '=' file.txt # 只显示行号
sed -n '$=' file.txt # 显示总行数
# 示例:显示行号及内容
sed '=' file.txt | sed 'N; s/\n/:/' # 格式:行号:内容
# 示例:处理特定行号范围
sed '10,20 { # 对10-20行执行多个操作
s/foo/bar/g
/baz/d
}' file.txt
4. 分组与反向引用
bash
# 使用括号分组和反向引用(需要 -r 或 -E)
sed -r 's/(foo)(bar)/\2\1/' file.txt # 交换foo和bar
sed -r 's/([0-9]{3})-([0-9]{3})-([0-9]{4})/(\1) \2-\3/' file.txt # 格式化电话
# 示例:提取和重组
echo "John Doe,30,Engineer" | sed -r 's/([^,]*),([^,]*),([^,]*)/姓名:\1, 年龄:\2, 职位:\3/'
基础练习
题目1:简单替换
文件 data1.txt 内容:
apple orange apple
banana apple grape
orange banana orange
要求:
-
将所有 "apple" 替换为 "fruit"
-
只替换每行第一个 "apple"
-
只替换每行第二个 "orange"
bash
sed -i 's/apple/fruit/g' data1.txt
sed -i 's/apple/fruit' data1.txt
sed -i 's/apple/fruit/2' data1.txt
题目2:行操作
文件 data2.txt 内容:
Line 1: First line
Line 2: Second line
Line 3: Third line
Line 4: Fourth line
Line 5: Fifth line
要求:
-
删除第3行
-
删除第2-4行
-
在 "Third line" 前插入 "INSERTED LINE"
-
在最后一行后追加 "APPENDED LINE"
sed -i '3d' data2.txt
sed -i '2,4d' data2.txt
sed '3a\APPENDED' data2.txt
题目3:模式匹配
文件 config.txt 内容:
# Server configuration
server_name = localhost
port = 8080
# Database settings
db_host = 127.0.0.1
db_port = 3306
# End of config
要求:
-
删除所有注释行(以 # 开头)
-
删除空行
-
只显示包含 "port" 的行
sed '/^#/d' config.txt
sed '/ /d' config.txt 错了这是删除空格的行! 正确:sed '/^$/d' config.txt
sed -n '/port/p' config.txt 没有考虑大小写 正确:sed -n '/port/ip' config.txt
中级练习
题目4:提取数据
文件 log.txt 内容:
2023-10-01 10:00:01 INFO User login: john@example.com
2023-10-01 10:00:05 ERROR Database connection failed
2023-10-01 10:00:10 INFO File uploaded: report.pdf
2023-10-01 10:00:15 WARNING Disk space low
2023-10-01 10:00:20 INFO User logout: john@example.com
要求:
-
提取所有 ERROR 行
-
提取时间戳(格式:10:00:01)
-
提取邮箱地址
-
提取文件名(不含路径)
sed -n '/ERROR/p' log.txt
sed -n 's/.* \([0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}\) .*/\1/p' log.txt
sed -n 's/.* \([^ ]*@[^ ]*\.[^ ]*\) .*/\1/p' log.txt
sed -n 's/.* \([^/ ]\+\)$/\1/p' log.txt
题目5:格式转换
文件 contacts.csv 内容:
"John Doe","john@example.com","555-1234"
"Jane Smith","jane@example.com","555-5678"
"Bob Johnson","bob@example.com","555-9012"
要求:
-
删除所有引号
-
将逗号分隔转换为管道分隔
-
转换格式为:姓名: <name>, 邮箱: <email>, 电话: <phone>
-
只显示姓名和邮箱
sed '/"/ /g' contacts.csv
sed 's/"//g; s/,/|/g' contacts.csv
sed 's/"//g' contacts.csv | awk -F, '{print "姓名: "1", 邮箱: "2", 电话: "$3}'
sed 's/"//g' contacts.csv | awk -F, '{print "姓名: "1", 邮箱: "2}'