Linux 下 sed 命令的核心用法与实战指南
概述
sed(Stream Editor,流编辑器)是 Linux 里干文本处理的一把好手,说白了就是个「流编辑器」——你不用真把文件打开手动改,写一段脚本指令,它就能自动帮你批量改、过滤、转换文本。
man 手册给它的官方定义是 “stream editor for filtering and transforming text”,也就是”用来过滤和转换文本的流编辑器”。平时它最常被拿来干这些活:
- 自动编辑单个或多个文本文件
- 简化对文件的重复性操作(如批量替换固定字符)
- 编写文本格式转换程序(如日志格式标准化)
- 配合管道(|)处理其他命令的输出结果
sed 核心参数解析
sed 的一堆参数主要用来控制命令怎么跑。把最常用的几个整理成下表,作用和使用场景都列清楚了:
| 参数 | 完整写法 | 核心作用 | 适用场景 |
|---|---|---|---|
| -n | - | 抑制模式空间的自动打印 | 仅显示匹配或处理后的内容(需配合 p 命令) |
| -e | —expression=script | 多点编辑 | 一次执行多个 sed 命令(如同时删除 + 替换) |
| -f | —file=/PATH/TO/SCRIPT | 从文件读取命令 | 命令较多时,将指令写入脚本文件批量执行 |
| -r | —regexp-extended | 支持扩展正则表达式 | 无需对 +、?、() 等符号转义(同 -E) |
| -i | —in-place[=SUFFIX] | 直接编辑原文件 | 需修改源文件时使用(加后缀可备份,如 -i.bak) |
sed 地址:定位要处理的行
sed 靠”地址”来圈定要动哪些行,地址怎么写直接决定了操作范围,想精准改文本全靠它。
| 地址类型 | 格式示例 | 作用说明 |
|---|---|---|
| 空地址 | 无 | 对文件所有行执行操作 |
| 单地址 | 5 | 仅处理第 5 行 |
| 单地址 | /error/ | 处理所有包含 “error” 模式(正则匹配)的行 |
| 地址范围 | 3,7 | 处理第 3 行到第 7 行(含首尾) |
| 地址范围 | 5,+3 | 处理第 5 行及后续 3 行(共 4 行:5/6/7/8) |
| 地址范围 | 2,/EOF/ | 从第 2 行开始,直到匹配 “EOF” 的行结束 |
| 地址范围 | /start/,/end/ | 从匹配 “start” 的行开始,到匹配 “end” 的行结束 |
| 地址范围 | $ | 仅处理文件的最后一行($ 表示行尾) |
| 步进(间隔) | 1~2 | 处理所有奇数行(从第 1 行开始,步长 2) |
| 步进(间隔) | 2~2 | 处理所有偶数行(从第 2 行开始,步长 2) |
sed 基础命令:实现核心操作
sed 的命令得配合前面的”地址”一起用,决定对被圈中的行具体干啥。常用的基础命令列在下面:
| 命令 | 语法格式 | 作用说明 |
|---|---|---|
| d | [地址]d | 删除定位到的行(不保留到输出) |
| p | [地址]p | 打印模式空间中的内容(需配合 -n 抑制默认输出) |
| a | [地址]a \text | 在定位行后面追加文本 “text”,支持 \n 换行(如 a hello\nworld) |
| i | [地址]i \text | 在定位行前面插入文本 “text”,支持 \n 换行 |
| c | [地址]c \text | 用 “text”替换定位到的所有行(整行替换) |
| w | [地址]w /PATH/FILE | 将定位到的行保存到指定文件(覆盖原文件内容) |
| r | [地址]r /PATH/FILE | 读取指定文件的内容,追加到定位行后面(实现文件合并) |
| = | [地址]= | 为定位到的行打印行号(单独输出在行首) |
| ! | [地址]!命令 | 条件取反:对 “不满足地址” 的行执行命令(如 /error/!d 表示 “除含 error 的行外都删除”) |
| s | [地址]s/原内容/新内容/标记 | 查找替换(核心命令),分隔符可自定义(如 s@@@、s###) |
替换命令(s)的关键标记
s/// 是 sed 里用得最频繁的功能,光是替换还不够,后面还能跟”标记”来加花样,常用的标记有这些:
- g:全局替换(默认仅替换每行第一个匹配项)
- w /PATH/FILE:将替换成功的行保存到指定文件
- p:显示替换成功的行(需配合 -n)
- i:忽略大小写匹配(如 s/error/INFO/gi 表示全局忽略大小写替换)
除此之外,替换时还能用两个特殊符号玩点花样:
- &:表示匹配到的完整内容(如 s/int/&eger/g 会将 “int” 替换为 “integer”)
- \n(n 为数字):引用正则中 () 捕获的第 n 个分组(如 s/(name):(\w+)/\2=\1/ 会将 “name:Tom” 替换为 “Tom=name”)
sed 高级命令:模式空间与保持空间
sed 肚子里维护着两块缓存:模式空间(Pattern Space) 和 保持空间(Hold Space)。模式空间就是临时放”当前正在处理的那一行”的地方;保持空间则像个临时仓库,先把数据搁着。那些高级命令,本质上就是在这两个空间之间倒腾数据,从而实现跨行处理、暂存数据这类复杂活。
| 命令 | 作用说明 |
|---|---|
| h | 将模式空间的内容覆盖到保持空间(原保持空间内容被清空) |
| H | 将模式空间的内容追加到保持空间(原内容后加换行符) |
| g | 将保持空间的内容覆盖到模式空间(原模式空间内容被清空) |
| G | 将保持空间的内容追加到模式空间(原内容后加换行符) |
| x | 交换模式空间与保持空间的内容 |
| n | 读取下一行文本覆盖到模式空间(替换当前行,跳过后续命令) |
| N | 读取下一行文本追加到模式空间(形成多行模式空间,可跨行处理) |
| d | 删除模式空间中的所有内容(终止当前循环,读取下一行) |
| D | 仅删除多行模式空间中的第一行(不终止循环,可继续处理剩余内容) |
提示:高级命令光看说明有点抽象,得结合实际场景才容易懂,比如 N+D 这套组合就能用来干”删空行""合并多行”这些事。
sed 实战示例:从基础到进阶
下面这些例子覆盖了日常里最常用到的 sed 场景。为了灵活,示例里都用变量(比如 $pattern、$path_to_file)代替具体值,你实际用时把它们换成真东西就行(比如 /var/log/nginx.log)。
1. 精准查看指定内容(抑制自动打印)
用 -n 加上 p 命令,就只把匹配上的行打出来,其他乱七八糟的内容都不显示:
# 显示包含指定模式的行
sed -n "/$pattern/p" $path_to_file
# 显示两个模式之间的所有行
sed -n "/$start_pattern/,/$end_pattern/p" $path_to_file
# 显示指定行
sed -n "${line_num}p" $path_to_file
# 显示行范围
sed -n "$from_line,$to_line)p" $path_to_file
2. 批量删除指定行
删除操作默认不会动原文件(除非加 -i),只是把处理后的结果打出来给你看:
# 删除第 N 行
sed "${line_num}d" $path_to_file
# 删除所有包含指定模式的行
sed "/$pattern/d" $path_to_file
# 删除最后一行
sed "$d" $path_to_file
3. 插入与替换行(整行操作)
3.1 在指定行后追加内容
# 在第 N 行后追加文本
sed "${line_num}a\ $content" $path_to_file
# 在包含模式的行后追加文本(支持换行)
sed "/$pattern/a hello\nworld" $path_to_file
3.2 在指定行前插入内容
# 在第 N 行前插入文本
sed "${line_num}i\ $content" $path_to_file
# 在包含模式的行前插入文本
sed "/$pattern/i $content" $path_to_file
3.3 替换指定行(整行替换)
# 用内容替换第 N 行
sed "${line_num}c\ $new_content" $path_to_file
# 用内容替换所有包含模式的行
sed "/$pattern/c\ $new_content" $path_to_file
4. 合并文件:插入外部文件内容
用 r 命令可以把另一个文件的内容插到指定位置,相当于把文件拼起来:
# 在每一行后插入外部文件内容
sed "r $external_file" $target_file
# 在第 N 行后插入外部文件内容
sed "${line_num}r $external_file" $target_file
# 在指定行范围后插入外部文件内容
sed "$from_line,$to_line r $external_file" $target_file
5. 保存处理结果到新文件
5.1 保存匹配行到新文件
# 将包含模式的行保存到新文件(原文件不变)
sed -n "/$pattern/w $new_file" $source_file
5.2 直接修改原文件(带备份)
# 直接修改原文件,并备份为 "原文件.bak"
sed -i.bak "s/$old/$new/g" $target_file
6. 查找替换:从基础到高级
6.1 基础替换(全局替换)
# 全局替换文件中所有 "old" 为 "new"
sed "s/$old/$new/g" $target_file
6.2 指定行替换
# 仅在第 N 行替换 "old" 为 "new"
sed "${line_num}s/$old/$new/g" $target_file
# 仅在包含模式的行中替换(忽略大小写)
sed "/$pattern/s/$old/$new/gi" $target_file
6.3 引用匹配内容(& 与分组)
# 用 & 引用匹配的完整内容(给 "link" 后加 "er")
sed 's/link/&er/g' $file
# 用 \1 引用分组内容(交换 "name:value" 为 "value=name")
sed 's/\(name\):\(.*\)/\2=\1/g' $file
7. 扩展用法:多点编辑与脚本执行
7.1 一次执行多个命令(多点编辑)
用 -e 可以一次跑多个命令,比如下面这组”删除 + 插入”的组合:
# 1. 在第 1 行前插入文本;2. 在第 1 行后追加文本
sed -e "1i $insert_content" -e "1a $append_content" $file
7.2 从脚本文件执行命令
命令一多,全挤在命令行里就太难看了,这时候可以把指令写成脚本文件(如 sed_script.sed):
# sed_script.sed 内容示例
# 1d # 删除第 1 行
# s/error/INFO/g # 全局替换 error 为 INFO
# /EOF/w result.txt # 将包含 EOF 的行保存到 result.txt
# 执行脚本文件
sed -f $sed_script $target_file
8. 配合正则:扩展正则的使用
用 -r(或者 -E 也行)就能开启扩展正则,这样 +、?、() 这些符号都不用转义了,直接写:
# 匹配 "error" 或 "warn"(扩展正则的 "|" 无需转义)
sed -r -n "/error|warn/p" $log_file
# 匹配 "num" 后跟 1-3 个数字(扩展正则的 "{n,m}" 无需转义)
sed -r "s/num([0-9]{1,3})/number\1/g" $file
总结
sed 算是 Linux 里做文本处理的核心工具了,它最大的好处就是自动化、高效、还灵活。只要把”地址定位 + 命令操作”这套核心逻辑弄明白,批量改文本、过滤日志、做格式转换这些活就都不在话下了。
建议你先从几个基础命令(s/d/p)和上面的示例入手,混熟了再去碰模式空间和保持空间那些高级玩法,慢慢把 sed 变成你日常干活的一部分,文本处理效率一下就上去了。