sed —— 流式替换与行处理

运维工具手册 04-文本处理 5.1 KB 原文

sed —— 流式替换与行处理

分类:04 文本处理 适用:全平台(GNU sed 4.x / BSD sed;-i 语法不同) 一句话:批量替换、删除、插入、按行范围取内容,改配置文件的利器。

1. 它解决什么问题

  • 批量把配置文件里的 8080 改成 9090。
  • 删除注释行和空行。
  • 取出日志里两个时间点之间的内容。
  • 在匹配行后追加一段配置。

2. 基础模型

sed [选项] '地址 命令' 文件

sed 逐行读入、执行命令、输出,默认每一行都会打印。

3. 替换(最常用)

sed 's/old/new/' file             # 每行第一处
sed 's/old/new/g' file            # 每行全部
sed 's/old/new/2' file            # 每行第二处
sed 's/old/new/gi' file           # 全局 + 忽略大小写
sed 's/old/new/g' -i file         # 原地修改(GNU)
sed -i.bak 's/old/new/g' file     # 原地修改并备份为 file.bak

macOS / BSD 的 -i

sed -i '' 's/old/new/g' file      # 必须给空后缀
sed -i '.bak' 's/old/new/g' file  # 备份

分隔符

路径里有 / 时换分隔符,避免转义地狱:

sed 's#/usr/local#/opt#g' file
sed 's|/var/log|/data/log|g' file
sed 's@old@new@g' file

反向引用

sed -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3\/\2\/\1/' file   # 2026-11-05 → 11/05/2026
sed -E 's/^(listen\s+)[0-9]+/\18080/' nginx.conf               # 保留前缀,改端口
sed -E 's/.*user=([a-z]+).*/\1/' file                          # 提取字段

& 表示整个匹配:

sed 's/ERROR/[ERROR]/' file        # 给匹配加括号
sed -E 's/[0-9]+/X&X/g' file

只替换匹配行的某部分

sed '/^server_name/ s/old.com/new.com/' nginx.conf
sed -n '/^\[mysqld\]/,/^\[/ s/^port=.*/port=3307/p' my.cnf

4. 删除与选择

sed '5d' file                  # 删第 5 行
sed '5,10d' file               # 删 5-10 行
sed '5,$d' file                # 删 5 到最后
sed '/^#/d' file               # 删注释行
sed '/^$/d' file               # 删空行
sed '/^#/d;/^$/d' file         # 删注释和空行(去注释)
sed '/pattern/d' file          # 删匹配行
sed '/pattern/!d' file         # 删不匹配行(= 只保留匹配行)
sed '2~3d' file                # 从第 2 行起每 3 行删一行(GNU)

-n + p 精确输出

sed -n '10,20p' file                        # 打印 10-20 行
sed -n '/BEGIN/,/END/p' file                # 打印两个标记之间
sed -n '$p' file                            # 最后一行
sed -n '1~2p' file                          # 奇数行(GNU)
sed -n '/ERROR/p' file                      # 相当于 grep
sed -n '/2026-11-05 10:00/,/2026-11-05 11:00/p' app.log

5. 插入、追加、修改

sed '3i\新的一行' file                  # 第 3 行前插入
sed '3a\新的一行' file                  # 第 3 行后追加
sed '3c\替换整行' file                  # 替换第 3 行
sed '/^\[mysqld\]/a\port=3307' my.cnf   # 匹配行后追加
sed '/^\[mysqld\]/i\# managed by ansible' my.cnf
sed '$a\末尾追加' file

多行插入(GNU):

sed '/^\[mysqld\]/a\port=3307\nbind-address=0.0.0.0' my.cnf

6. 读取与写入文件

sed -n '1r header.txt' file            # 第 1 行后插入文件内容
sed '/pattern/w /tmp/matched.txt' file # 匹配行写入另一个文件

7. 其他常用命令

sed -n '=' file                        # 输出行号
sed 'y/abc/xyz/' file                  # 字符一一替换
sed -E 's/^[ \t]+//' file              # 去行首空白
sed -E 's/[ \t]+$//' file              # 去行尾空白
sed '/^$/N;/^\n$/D' file               # 合并连续空行为一行
sed ':a;N;$!ba;s/\n/ /g' file          # 全文合并为一行
sed 'G' file                           # 每行后加空行

8. 实战场景

8.1 批量改配置(带备份与确认)

cp nginx.conf nginx.conf.bak
sed -n '/listen/p' nginx.conf                       # 改前确认
sed -i 's/listen 8080/listen 9090/g' nginx.conf
sed -n '/listen/p' nginx.conf                       # 改后核对
nginx -t

8.2 去掉配置文件注释(保留有效配置)

sed -E '/^[[:space:]]*#/d; /^[[:space:]]*$/d' app.conf

8.3 取某个时间段的日志

sed -n '/2026-11-05 14:00/,/2026-11-05 15:00/p' app.log > /tmp/slice.log

比 awk 简单,但注意首尾行若不存在会输出从匹配到最后。

8.4 给文件每行加前缀(做 SQL 批量)

sed 's/^/INSERT INTO t VALUES (/' ids.txt | sed 's/$/);/' > insert.sql
# 或一步
sed -E "s/^(.*)$/INSERT INTO t VALUES ('\1');/" ids.txt

8.5 修改 YAML 里的镜像版本(临时,长期用 yq)

sed -i -E 's#(image: .*:)[0-9]+\.[0-9]+\.[0-9]+#\11.2.3#' deploy.yaml

YAML 缩进/多文档场景 sed 容易改错,规范做法是 yq。

8.6 安全替换:先看会改哪些行

sed 's/old/new/g' file | diff - file        # 预览差异
sed -i.bak 's/old/new/g' file && diff file.bak file

9. 常见坑

  1. -i 在 macOS 必须带参数:sed -i '',否则会把脚本当后缀。
  2. -i 不备份:生产改配置一律 -i.bak,或先 cp。
  3. 默认只替换每行第一处:忘了 /g 是最高频错误。
  4. 特殊字符要转义:&、\、/、.、*、[、]。
  5. & 是"整个匹配":想插入字面量 & 要写 \&。
  6. 正则贪婪:s/.*=// 会删到最后一个 =,要精确用 [^=]*。
  7. BRE 与 ERE:GNU 默认 BRE,+、?、|、() 要转义或用 -E。
  8. sed -i 会破坏软链接:它替换文件而非修改 inode,软链接会变成普通文件;用 --follow-symlinks(GNU)。
  9. 处理大文件:sed 流式处理没问题,但 -i 需要临时空间,/tmp 满会失败。
  10. 中文按字节处理:sed 's/./X/g' 在多字节字符上会乱,用 -E 配合 [^[:space:]] 或改用 perl -CS。
  11. 空文件/无匹配:sed 不报错,退出码仍为 0,脚本里别用它判断"是否改成功"。
  12. sed -n '1,10p' 与 head -10 不同:前者仍读整个文件,大文件用 head 更快。

10. 速查表

sed 's/a/b/' f                  # 首个替换
sed 's/a/b/g' f                 # 全部替换
sed -i.bak 's/a/b/g' f          # 原地+备份
sed -E 's/(x)(y)/\2\1/' f       # 扩展正则+反向引用
sed 's#/a#/b#g' f               # 换分隔符
sed -n '10,20p' f               # 取行
sed -n '/start/,/end/p' f       # 取区间
sed '/^#/d;/^$/d' f             # 去注释空行
sed '/pat/d' f                  # 删匹配行
sed '/pat/!d' f                 # 只留匹配行
sed '3a\line' f                 # 追加
sed '3i\line' f                 # 插入
sed '3c\line' f                 # 替换整行
sed -n '=' f                    # 行号
sed 's/^[ \t]*//' f             # 去行首空白
sed —— 流式替换与行处理 - 知识库 - 起航社区