人人都会AI编程

9.2 sed:流编辑器

更新时间:2026-07-12

sed(stream editor)是一种非交互式的文本处理工具,它逐行读取输入流,按照给定的指令修改内容,然后把处理后的结果输出。因为不需要打开完整的可视界面,sed 非常适合在管道线里与其他命令配合,批量处理日志、配置文件和大批量文本。

1. 基本工作方式

sed 对每一行文本依次执行“匹配地址 → 执行命令”的流程。如果地址匹配成功,相应命令就会作用于该行;否则原样输出(或者根据 -n 选项禁止默认输出)。常用的使用形式是:

sed [选项] '地址命令' 文件...
sed [选项] -e '命令1' -e '命令2' 文件...

如果省略文件,sed 会从标准输入读取数据,这样就可以直接通过管道接收上游命令的输出。

2. 常用选项(务实版)

  • -n:禁止默认输出,只有经过明确打印命令(p)的行才会被输出。这在只想提取特定行时极其常用。
  • -e:指定多条编辑命令,后接命令字符串。
  • -i直接修改原文件(谨慎使用)。GNU sed 下若在 -i 后添加扩展名,例如 -i.bak,则会在修改前创建备份文件(如 原文件.bak)。
  • -r-E:启用扩展正则表达式,使 +?(){} 等符号默认具有正则含义,而不用大量加反斜线。

3. 地址与命令的组合

地址可以是行号、正则表达式或范围。命令则有 s(替换)、d(删除)、p(打印)、a(后插)、i(前插)、c(替换整行)等。最核心的用法是替换和选择性输出。

3.1 行号定位

  • 输出文件第 3 行到第 8 行:
  sed -n '3,8p' log.txt
  
  • 从第 5 行到文件末尾全部删除(不输出):
  sed '5,$d' file.txt
  

3.2 正则定位

  • 删除所有包含 DEBUG 的行:
  sed '/DEBUG/d' app.log
  
  • 输出从第一个匹配 START 的行到第一个匹配 END 的行之间的内容:
  sed -n '/START/,/END/p' data.txt
  

4. 替换命令 s:核心竞争力

s 命令的格式为 s/正则/替换内容/修饰符。分隔符不仅限于 /,也可以用 #| 等减少转义麻烦。

  • 简单替换:把每个 apple 替换为 orange
  sed 's/apple/orange/' fruits.txt
  

注意每行只替换第一次出现。要全局替换g 修饰符:

  sed 's/apple/orange/g' fruits.txt
  
  • 使用正则捕获组:将 key=value 格式转换为 key: value
  sed 's/\(.*\)=\(.*\)/\1: \2/' config.txt
  

若用了 -E(即 -r),写法更清晰:

  sed -E 's/(.*)=(.*)/\1: \2/' config.txt
  
  • 选择性替换:只对匹配 server 的行进行替换
  sed '/server/s/80/443/' nginx.conf
  
  • 使用不同分隔符:替换路径时用 # 避免转义
  sed 's#/home/user#/opt/app#' paths.txt
  
  • 大小写不敏感替换:加上 i 修饰符
  sed 's/error/ERROR/gi' log.txt
  

5. 实战场景

场景一:快速修改配置文件
要关闭 SSH 的密码认证,需把 #PasswordAuthentication yesPasswordAuthentication yes 改为 PasswordAuthentication no,并去掉行首注释:

sed -E 's/^#?(PasswordAuthentication).*/\1 no/' /etc/ssh/sshd_config

确认无误后加上 -i 直接写入文件:

sudo sed -i -E 's/^#?(PasswordAuthentication).*/\1 no/' /etc/ssh/sshd_config

场景二:清理日志中的 IP 地址
只提取每行日志中第一个出现的 IP:

sed -n 's/.*\([0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\).*/\1/p' access.log

若使用 -E,正则可以写得简短:

sed -nE 's/.*([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}).*/\1/p' access.log

场景三:批量重命名文件(生成脚本)
sed 生成重命名命令,检查无误后执行:

ls *.txt | sed 's/\(.*\)\.txt/mv \1.txt \1.md/' 

输出类似 mv note.txt note.md 的行,确认后通过 sh 执行:

ls *.txt | sed 's/\(.*\)\.txt/mv \1.txt \1.md/' | sh

这是一种安全做法——先看到生成的命令,再决定是否提交执行。

场景四:多行插入与追加
在某行下追加一行:

sed '/\[mysqld\]/a max_connections = 500' my.cnf

在某行前插入一行:

sed '/\[mysqld\]/i # 数据库配置' my.cnf

6. 与其它工具的组合

sed 生来就在管道中发挥作用。例如结合 findxargs 批量替换多个文件中的字符串:

find . -name "*.html" -print0 | xargs -0 sed -i 's/old-domain.com/new-domain.com/g'

或者从 grep 的输出中筛选内容并进一步处理:

grep 'ERROR' app.log | sed 's/.*msg="\([^"]*\)".*/\1/'

先通过 grep 过滤包含 ERROR 的行,再用 sed 提取引号内的错误描述。

7. 实用要点

  • sed 默认将结果输出到标准输出(屏幕),不会改动原文件,安全试验时可去掉 -i 先预览。
  • 使用 -i 要格外小心,尤其是与 * 通配符联用时,建议先备份或使用 -i.bak
  • sed 支持在脚本中使用 -f 选项从文件读取多条编辑命令,适合复杂、可复用的编辑流程。
  • 当一行内有多处替换需求时,务必记得加 g 标记,否则只会替换第一个匹配。
  • 熟练掌握 sed 后,许多原本需要打开编辑器才能完成的小修改(如去除行尾空格、统一缩进、删除空行等)都可以在命令行瞬间完成,效率显著提升。

sed 的哲学和 Linux 其他核心工具一致:不追求一次性解决所有问题,而是做好流式文本替换这件事,让你能把它作为构建块嵌入到更大的自动化流程中。