sed 命令详解:文本编辑流处理实战

前言

sed(Stream Editor,流编辑器)是 Linux/Unix 三大文本处理工具之一,擅长对文本做逐行编辑:替换、删除、插入、打印。它和 awk 的最大区别在于------sed 更专注于"编辑",awk 更专注于"分析计算"。

很多同学对 sed 的印象停留在 sed 's/old/new/',但 sed 的能力远不止替换。本文系统讲解 sed 的语法、地址定位、编辑命令、模式空间与保持空间、多行处理,最后配实战案例。

本文基于 GNU sed,CentOS / Ubuntu 默认自带。文末会专门说明 GNU sed 与 Mac BSD sed 的差异。


目录


一、sed 是什么

sed 的设计哲学 :把输入当作文本流,逐行 读取到"模式空间",在模式空间中执行编辑命令,然后把结果输出。默认不修改原文件。

特点:

  • 非交互式(脚本化、可批量)
  • 按行处理,内存占用小
  • 编辑能力强,适合"改"文本
  • 配合正则,威力巨大

一句话定位:

  • 想找 文本 → grep
  • 想改 文本 → sed
  • 想算 数据 → awk

二、基本语法

复制代码
sed [选项] '地址 命令' 文件
sed [选项] -e '命令1' -e '命令2' 文件
sed [选项] -f script.sed 文件
  • 地址:决定命令作用在哪些行(可省略,省略则作用于所有行)
  • 命令 :p d s a i c 等编辑动作
  • 选项 :-n -i -e -f -r 等

最简单的例子

bash 复制代码
# 替换每行第一个匹配的 old 为 new
sed 's/old/new/' file.txt

# 全局替换(每行所有匹配)
sed 's/old/new/g' file.txt

# 打印第 5 行
sed -n '5p' file.txt

# 删除第 3 到 10 行
sed '3,10d' file.txt

三、工作流程:模式空间与保持空间

理解 sed 的核心是理解两个概念:

复制代码
┌──────────────────────────────────────────────┐
│                 输入流                        │
└────────────────┬─────────────────────────────┘
                 ▼
        ┌────────────────┐
        │   读入一行      │
        └────────┬───────┘
                 ▼
┌──────────────────────────────────────────────┐
│              模式空间 (Pattern Space)          │
│   当前正在处理的"工作区",命令都在这里执行     │
└────────┬──────────────────────────────────────┘
         │  执行完该行的所有命令
         ▼
┌────────────────┐         ┌────────────────────┐
│ 输出结果(默认)  │         │  保持空间 (Hold     │
└────────┬───────┘         │  Space)------额外的     │
         │                 │  暂存区,跨行保存    │
         ▼                 └────────────────────┘
      下一行
  • 模式空间:每一行读进来处理的工作区,处理完默认输出,然后清空
  • 保持空间:一个额外的暂存区,可以跨行保存数据(类似"剪贴板")

关键 :默认情况下,sed 处理完一行后输出模式空间的内容 。这是很多人用 sed -n 时困惑的根源。


四、地址定位

地址决定"命令作用在哪些行",有四种写法:

4.1 单个行号

bash 复制代码
sed '5d' file.txt          # 删除第 5 行
sed -n '3p' file.txt       # 打印第 3 行

4.2 行号范围

bash 复制代码
sed '3,7d' file.txt        # 删除第 3 到 7 行
sed -n '10,20p' file.txt   # 打印第 10 到 20 行
sed '1,/pattern/d' file.txt # 从第 1 行删到第一次匹配 pattern

4.3 正则匹配

bash 复制代码
sed '/ERROR/d' file.txt          # 删除包含 ERROR 的行
sed '/^#/d' file.txt             # 删除注释行
sed '/^$/d' file.txt             # 删除空行
sed '/start/,/end/d' file.txt    # 删除从 start 到 end 之间的行

4.4 组合与取反

bash 复制代码
# 多地址组合
sed '/^#/d; /^$/d' file.txt      # 删除注释行和空行

# 取反 !(对不匹配的行执行命令)
sed '/ERROR/!d' file.txt         # 只保留包含 ERROR 的行(等同 grep ERROR)

# 行号 + 正则
sed '1,5s/old/new/' file.txt     # 只在前 5 行做替换

# 步长(GNU 扩展)
sed '1~2p' file.txt              # 打印奇数行(第 1,3,5... 行)
sed '0~3d' file.txt              # 删除每 3 行中的第 3 行

五、核心编辑命令

命令 含义 示例
p 打印 sed -n '5p'
d 删除 sed '/^$/d'
s 替换 sed 's/old/new/'
a\ 在当前行后追加 sed '/pattern/a\new line'
i\ 在当前行前插入 sed '/pattern/i\new line'
c\ 替换整行 sed '/pattern/c\replacement'
y 字符转换(类似 tr) sed 'y/abc/ABC/'
= 打印行号 sed '/pattern/='
q 退出 sed '10q'
r file 读入文件并插入 sed '/pattern/r insert.txt'
w file 写入文件 sed '/pattern/w out.txt'
n 读入下一行 ---
N 追加下一行到模式空间 ---
h/H 复制/追加模式空间到保持空间 ---
g/G 复制/追加保持空间到模式空间 ---
x 交换模式空间和保持空间 ---

5.1 追加 / 插入 / 替换整行

bash 复制代码
# 在匹配行后面追加
sed '/^\[section\]/a\new_key = value' config.conf

# 在匹配行前面插入
sed '/^server {/i\# 这是注释' nginx.conf

# 替换整行
sed '/old_line/c\new_line_content' file.txt

# 多行追加(GNU 扩展用 \n)
sed '/pattern/a\line1\nline2' file.txt

5.2 字符转换 y

bash 复制代码
# 类似 tr:a→A, b→B, c→C
sed 'y/abc/ABC/' file.txt

# 把所有小写转大写
sed 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' file.txt

5.3 打印行号

bash 复制代码
# 在匹配行前面打印行号
sed '/ERROR/=' app.log

# 打印行号 + 内容(类似 cat -n | grep)
sed -n '/ERROR/{=;p}' app.log

5.4 读入与写出

bash 复制代码
# 在匹配行后插入另一个文件的内容
sed '/#INSERT/r extra.conf' main.conf

# 把匹配的行写到文件
sed -n '/ERROR/w errors_only.log' app.log

六、替换命令 s 深度解析

s 是 sed 最常用的命令,完整语法:

复制代码
s/正则/替换/标志

6.1 标志位

标志 含义
g 全局替换(一行所有匹配)
n 只替换第 n 个匹配
p 只打印发生替换的行(配合 -n)
i 或 I 忽略大小写
w file 把替换结果写到文件
bash 复制代码
# 只替换每行第 2 个匹配
sed 's/old/new/2' file.txt

# 全局 + 忽略大小写
sed 's/error/ERROR/gi' file.txt

# 只打印发生替换的行
sed -n 's/old/new/p' file.txt

6.2 分隔符选择

s 命令的分隔符不一定要用 /,可以用任意字符(通常用 # 或 |):

bash 复制代码
# 路径里带斜杠,用 # 做分隔符更清晰
sed 's#/old/path#/new/path#g' file.txt

# 或 | 
sed 's|http://old.com|https://new.com|g' file.txt

6.3 分组与后向引用

用 \(...\)(基础正则)或 ()(扩展正则 -r)捕获分组,用 \1 \2 引用:

bash 复制代码
# 交换第一列和第二列
sed -r 's/([^,]*),([^,]*)/\2,\1/' data.csv

# 给版本号加 v 前缀
sed -r 's/([0-9]+\.[0-9]+\.[0-9]+)/v\1/' versions.txt

# 提取并重组日期
sed -r 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3\/\2\/\1/' dates.txt

6.4 & 引用整个匹配

bash 复制代码
# 给所有数字加方括号
sed -r 's/[0-9]+/[&]/g' file.txt

# 把 ERROR 变成 **ERROR**
sed 's/ERROR/**&**/g' app.log

6.5 特殊替换(GNU 扩展)

bash 复制代码
# \u \l 转大写/小写下一个字符
sed 's/\([a-z]\)/\u&/g' file.txt    # 每个单词首字母大写

# \U \L 转大写/小写后续所有字符
sed 's/\(.*\)/\U&/' file.txt         # 整行转大写

# \1\2 等后向引用

七、多行处理

sed 默认按行处理,但有些场景需要跨行(比如 XML、多行注释)。

7.1 相关命令

命令 作用
N 读入下一行并追加到模式空间(用换行符 \n 连接)
P 打印模式空间的第一行(到第一个换行符)
D 删除模式空间第一行,然后回到脚本开头
n 读入下一行替换当前模式空间

7.2 跨行替换

bash 复制代码
# 把 "line1\nline2" 替换成 "line1 line2"
sed 'N;s/\n/ /' file.txt

# 删除多行注释 /* ... */
sed '/\/\*/,/\*\//d' code.c

# 合并连续的空行为一个
sed '/^$/{N;/^\n$/d}' file.txt

7.3 滑动窗口

bash 复制代码
# 每次处理两行,实现"两两合并"
sed 'N;s/\n/ /' file.txt

八、保持空间实战

保持空间是 sed 的"高级特性",用于跨行保存数据。

8.1 命令对照

命令 含义
h 模式空间 → 复制 → 保持空间(覆盖)
H 模式空间 → 追加 → 保持空间
g 保持空间 → 复制 → 模式空间(覆盖)
G 保持空间 → 追加 → 模式空间
x 交换模式空间和保持空间

8.2 翻转文件(倒序输出)

bash 复制代码
# 把每一行追加到保持空间,最后整体取回
sed '1!G;h;$!d' file.txt

原理拆解:

  • 1!G:除第 1 行外,把保持空间追加到模式空间
  • h:把模式空间复制到保持空间
  • $!d:除最后一行外,删除模式空间(不输出)

8.3 去重(类似 uniq,需配合排序)

bash 复制代码
# 简单的相邻去重
sed '$!N; /^\(.*\)\n\1$/!P; D' file.txt

8.4 行列转换

bash 复制代码
# 把多行合并成一行(逗号分隔)
sed ':a;N;$!ba;s/\n/,/g' file.txt

九、常用选项

选项 含义
-n 禁止默认输出(配合 p 使用)
-i[SUFFIX] 原地编辑(可指定备份后缀)
-e 多个编辑命令
-f 从脚本文件读取命令
-r 或 -E 使用扩展正则(无需转义 () {})
-l N 指定行长度(配合 l)
-s 将多个文件视为独立(GNU)
-z 用 NUL 分隔记录(处理含空格文件名)

9.1 -i 原地编辑

bash 复制代码
# 直接修改文件(危险!)
sed -i 's/old/new/g' file.txt

# 修改前先备份(推荐!)
sed -i.bak 's/old/new/g' file.txt
# 完成后检查,确认无误再删除备份
rm -f file.txt.bak

9.2 -e 多命令

bash 复制代码
sed -e 's/old/new/g' -e '/^#/d' -e '/^$/d' file.txt

9.3 -f 脚本文件

bash 复制代码
# script.sed 内容:
# s/old/new/g
# /^#/d
# /^$/d

sed -f script.sed file.txt

9.4 -r 扩展正则

bash 复制代码
# 基础正则(需要转义)
sed 's/\([0-9]\+\)/\1/' file.txt

# 扩展正则(不用转义)
sed -r 's/([0-9]+)/\1/' file.txt

十、实战案例

案例 1:批量注释/取消注释配置文件

bash 复制代码
# 注释掉包含某个配置的行
sed -i '/^#/!s/^/# /' config.conf

# 取消注释
sed -i 's/^# //' config.conf

# 取消特定行的注释
sed -i '/parameter/s/^# //' config.conf

案例 2:在文件首尾添加内容

bash 复制代码
# 在文件第一行前插入
sed -i '1i\# 这是文件头' file.txt

# 在文件最后一行后追加
sed -i '$a\# 这是文件尾' file.txt

# 用 echo 更简单(但 sed 更灵活)

案例 3:删除行首尾空白

bash 复制代码
sed 's/^[ \t]*//;s/[ \t]*$//' file.txt

案例 4:删除注释行和空行

bash 复制代码
sed '/^#/d;/^$/d' file.txt

案例 5:提取特定行之间的内容

bash 复制代码
# 提取 server {} 块
sed -n '/server {/,/}/p' nginx.conf

# 提取第 N 到 M 行
sed -n '100,200p' large_file.txt

案例 6:批量重命名(配合 shell)

bash 复制代码
# 把 .txt 改成 .bak
for f in *.txt; do
    mv "$f" "$(echo "$f" | sed 's/\.txt$/.bak/')"
done

案例 7:替换多行 XML

bash 复制代码
# 把 <name>old</name> 改成 <name>new</name>
sed -r 's|<name>[^<]+</name>|<name>new</name>|g' config.xml

案例 8:统计替换次数

bash 复制代码
# 打印每次替换(间接统计)
sed -n 's/old/new/gp' file.txt | wc -l

案例 9:条件替换(只替换匹配某模式的行)

bash 复制代码
# 只在 production 块里替换
sed '/production/,/^}/ s/debug = true/debug = false/' app.conf

案例 10:保留匹配行及前后 N 行(类似 grep -A -B)

bash 复制代码
# 打印匹配行及前后各 2 行
sed -n '/pattern/{x;p;x;p;n;p;n;p;n;p;}' file.txt

十一、常见坑与最佳实践

坑 1:-i 不备份直接改文件

  • 错误做法 :sed -i 's/a/b/' config.conf(改坏了没法恢复)
  • 正确做法 :sed -i.bak 's/a/b/' config.conf 然后验证再删 .bak

坑 2:Mac BSD sed 的 -i 必须带参数

bash 复制代码
# GNU sed(Linux)
sed -i 's/a/b/' file.txt

# BSD sed(Mac)------ -i 后面必须跟备份后缀,空字符串也要写
sed -i '' 's/a/b/' file.txt

坑 3:正则特殊字符没转义

bash 复制代码
# 点号 . 在正则里匹配任意字符
sed 's/192.168.1.1/new/' file.txt   # 错误

# 正确做法:转义或用 grep 精确匹配
sed 's/192\.168\.1\.1/new/' file.txt

坑 4:替换内容含斜杠 /

bash 复制代码
# 路径里的斜杠会打断命令
sed 's//old//new/' file.txt   # 语法错误

# 换分隔符
sed 's#/old/#/new/#' file.txt

坑 5:sed 不支持非贪婪匹配

  • sed 的正则是贪婪 的,.* 会匹配到最远
  • 需要非贪婪时用 [^<]* 替代 .*

坑 6:默认会修改所有匹配

bash 复制代码
# 只想替换每行第一个匹配,不要 g
sed 's/old/new/' file.txt      # 每行只替换第一个
sed 's/old/new/g' file.txt     # 每行替换所有

最佳实践

  1. 先不加 -i 跑一遍 ,确认输出正确再加 -i
  2. -i.bak 留备份,确认无误再删
  3. 复杂正则用 -r(扩展正则),少写一堆反斜杠
  4. 需要"算"就用 awk,sed 不适合做计算
  5. 大文件测试:先在文件副本上跑

十二、sed vs awk vs grep 选型

场景 首选工具 理由
找含某模式的行 grep 最快、最简单
替换文本(s 命令) sed 专精替换
按列提取、统计 awk 字段处理能力最强
删除/插入/追加行 sed a/i/c 命令直观
复杂条件 + 计算 awk 有完整的流程控制
批量文件编辑 sed -i 原地编辑方便

经验法则:

  • 能用 grep 解决的简单需求 → 用 grep
  • 需要"改"文本 → sed
  • 需要"算"或"按列处理" → awk

十三、速查表

常用一行命令

bash 复制代码
# 替换
sed 's/old/new/g' file.txt
sed -i.bak 's/old/new/g' file.txt

# 删除
sed '/pattern/d' file.txt          # 删匹配行
sed '5d' file.txt                  # 删第5行
sed '3,10d' file.txt               # 删3-10行
sed '/^#/d' file.txt               # 删注释行
sed '/^$/d' file.txt               # 删空行

# 打印
sed -n '5p' file.txt               # 打印第5行
sed -n '10,20p' file.txt           # 打印10-20行
sed -n '/start/,/end/p' file.txt   # 打印范围

# 插入/追加
sed '3i\new line' file.txt         # 第3行前插入
sed '$a\new line' file.txt         # 末尾追加

# 提取
sed -n '/pattern/,+5p' file.txt    # 匹配行及后5行
sed -n '/pattern/,/pattern/p' file.txt

# 去重
sed -i 's/old/new/g' file.txt

标志位速查

标志 含义
g 全局
i 忽略大小写
p 打印替换的行
w 写入文件
n 第 n 个匹配

结语

sed 是一门"小而精"的编辑语言。掌握 s 命令、地址定位、保持空间,你就能应对绝大多数文本编辑需求。但记住------sed 不是万能的,复杂的数据分析交给 awk,简单的查找交给 grep。

学习路径:

复制代码
基础:s 替换、d 删除、p 打印
  ↓
进阶:地址定位、正则分组、标志位
  ↓
高级:多行处理 N/D/P、保持空间 h/H/g/G/x
  ↓
实战:配置文件编辑、批量替换、文本清洗

版权声明:本文为作者原创,转载请注明出处。如有问题欢迎在评论区交流讨论。

推荐阅读


觉得有用的话,点个赞 👍 和收藏 ⭐ 支持一下吧!你最常用的 sed 一行命令是什么?欢迎在评论区分享~

相关推荐
木卫二号Coding1 小时前
第八十六篇-Strata 部署指南(Ubuntu 22.04 + Tesla V100【单卡】)
linux·运维·人工智能·ubuntu
码完就睡2 小时前
Linux——线程的创建与同步
linux·运维
Lsetea2 小时前
OpenSSL读取加密私钥报bad decrypt:passin与口令来源排查
linux·https·ssl证书·openssl·私钥
FY@-@2 小时前
Shell 脚本编程:条件判断与循环
linux·运维·前端
Peregrine92 小时前
Linux 基本指令大全
linux·运维·服务器
拂拉氏2 小时前
【知识讲解】 Linux动静态链接认识
linux·动静态链接
Huangjin007_2 小时前
【Linux 系统篇(二十八)】文件(五):Ext 系列文件系统(下):Ext2块组结构,inode与文件寻址原理
linux·运维·服务器
javachen__3 小时前
JsonView:一套VO实现多角色字段返回
java·linux·服务器
睡一觉就好了。3 小时前
TcpSocket
linux