字数 4801,阅读大约需 25 分钟
sed 是 Linux 下处理文本的「瑞士军刀」,而 GNU sed是其主流版本,作为流编辑器它仅对输入做一次遍历,效率极高,尤其适合管道过滤、批量修改配置、处理日志等场景,是 Shell 脚本自动化的必备工具。
GNU sed 是Stream Editor(流编辑器)的实现版本,用于对输入流(文件/管道/标准输入)执行基础文本转换,核心特点是边读、边处理、边输出,全程仅遍历一次输入,处理大文件时速度极快且不占满内存。
小文本手动编辑用vim,批量文本处理、脚本自动化、管道过滤用sed,典型场景:
GNU sed 有两种核心调用方式,日常轻量需求用直接执行命令,复杂逻辑用读取脚本文件,未指定输入文件或输入为-时,处理标准输入:
# 方式1:直接执行命令(最常用)
sed [核心选项] '处理脚本' 输入文件1 输入文件2...
# 方式2:从文件读取脚本(适合多命令复杂逻辑)
sed [核心选项] -f 脚本文件 输入文件1 输入文件2...输出规则:默认打印到标准输出(仅预览结果,不修改原文件),可通过-i实现原地编辑,-n抑制默认输出。
这是sed的「高频搭档」,区分基础必用和进阶实用,优先掌握前者:
-n | --quiet/--silent | ||
-i[后缀] | --in-place[=后缀] | -i.bak生成.bak备份,安全无风险 | |
-E | -r/--regexp-extended | ||
-e | --expression=脚本 | ||
-f | --file=脚本文件 | ||
--debug | |||
-s | --separate | ||
-z | --null-data | find -print0等场景,处理含空格的文件名 | |
--sandbox | e/w/r外部调用 | ||
--version/--help | |||
-b/--binary |
执行后可通过echo $?查看退出码,用于脚本中判断执行结果:
--posix的GNU扩展;q/Q指定,如sed 'Q42' 文件名,退出码为42。sed的所有操作都是**「针对指定行执行指定命令」**,这是核心逻辑,而地址定位就是告诉sed「处理哪一行/哪些行」,脚本结构则规范了命令的执行格式。
sed脚本由一个或多个命令组成,核心格式为:[地址]命令[选项]
!取反,对不匹配的行执行命令;;、换行分隔,或用-e指定多个脚本。地址定位支持行号、正则、范围三种形式,可单独使用也可组合,加!实现「取反处理」,是sed的核心基础,必须吃透:
基于行号的硬定位,简单粗暴,GNU sed提供扩展语法适配批量行:
5:仅处理第5行;$:仅处理最后一行;1~2:奇数行(GNU扩展),~后为步长;0~2:偶数行(GNU扩展);5,+3:第5行及后续3行(共4行)。用正则表达式模糊筛选行,是最常用的定位方式,GNU sed支持多种扩展语法,避免转义和适配不同场景:
/正则/:匹配正则表达式的行(默认基本正则BRE,+/?|()需转义);\%正则%:用%作为分隔符,避免内容中/的转义(如匹配路径时);/正则/I:忽略大小写匹配(GNU扩展);/正则/M:多行模式,^/$可匹配换行符前后(GNU扩展)。-E选项后使用扩展正则ERE,+/?|()无需转义,推荐日常使用。将「行号」和「正则」组合,用逗号,分隔,指定处理的行范围,适配批量连续行处理:
4,17:第4行到第17行;/start/,/end/:从匹配start的行到匹配end的行;2,/sed/:从第2行到第一个匹配sed的行;0,/regex/:从首行到第一个匹配regex的行(GNU扩展)。在任意地址后加!,表示对不匹配该地址的行执行命令,适合「排除式处理」:
/sed/!p:打印所有不含sed的行;3,5!d:删除除了第3-5行的所有行。创建测试文件,后续所有基础示例均基于此,先执行创建:
cat > test.txt << EOF
1. Hello World
2. sed is a stream editor
3. learn sed, use sed
4. hello sed
5. test text
EOF基于测试文件的地址定位示例,直观理解用法:
# 行号定位:处理第3行
sed '3s/sed/SED/' test.txt
# 正则定位:处理含hello的行(忽略大小写)
sed -E '/hello/I s/hello/HELLO/' test.txt
# 范围定位:处理第2行到第4行
sed '2,4s/.*/\U&/' test.txt
# 取反定位:处理不含sed的行,行尾追加[no sed]
sed '/sed/!s/$/ [no sed]/' test.txtsed的命令有很多,但90%的日常需求都围绕s替换命令,掌握它就掌握了sed的半壁江山,GNU sed对s命令提供了丰富的扩展,让文本替换更灵活、精准。
[地址]s/匹配内容/替换内容/标记核心技巧:分隔符/可替换为任意单个字符(如#、%、@),当匹配/替换内容中包含/(如路径)时,用其他分隔符可避免大量反斜杠转义,这是日常必用技巧:
# 替换路径,用/做分隔符需转义,繁琐
sed 's/\/home\/test/\/data\/test/' 文件名
# 用#做分隔符,无需转义,简洁
sed 's#/home/test#/data/test#' 文件名标记加在最后一个分隔符后,可单个使用或组合使用(如gp、Ig),是s命令的「灵魂」,GNU sed的扩展标记大幅提升实用性:
g | s/sed/SED/g | ||
s/sed/SED/2 | |||
p | -n | sed -n 's/sed/SED/gp' | |
w 文件名 | s/sed/SED/gw result.txt | ||
I/i | s/hello/你好/I | ||
M/m | ^/$匹配换行符前后 | ||
e |
在「替换内容」中可使用特殊符号,实现引用匹配内容、大小写转换等高级操作,无需重复编写匹配规则,是提升效率的关键:
&:引用匹配到的整个文本,适合给匹配内容加前缀/后缀;\N:引用正则中第N个\( \)分组的内容(-E扩展正则中无需转义()),适合调整内容顺序、提取指定部分。示例:
# &:给所有sed加中括号
sed 's/sed/[&]/g' test.txt
# \N:交换Hello和World(-E扩展正则,无需转义())
echo "Hello World" | sed -E 's/(\w+) (\w+)/\2 \1/'
# 输出:World Hello用于替换内容的格式化,\L/\U持续生效,直到**\E结束,\l/\u**仅对下一个字符生效,GNU sed专属扩展,非常实用:
\U:后续内容全部转为大写,\E结束;\u:仅下一个字符转为大写;\L:后续内容全部转为小写,\E结束;\l:仅下一个字符转为小写;\E:结束大小写转换。示例:
echo "hello world" | sed 's/.*/\U&/' # 全部大写:HELLO WORLD
echo "HELLO WORLD" | sed 's/.*/\L&/' # 全部小写:hello world
echo "hello world" | sed 's/\w\+/\u&/g' # 首字母大写:Hello World
echo "Hello World" | sed -E 's/(\w+) (\w+)/\U\1\E \L\2/' # HELLO world# 1. 全局替换,忽略大小写,仅打印替换行
sed -n 's/sed/SED/Igp' test.txt
# 2. 仅替换第2-4行的第一个hello为HELLO
sed '2,4s/hello/HELLO/' test.txt
# 3. 替换路径并将结果写入文件
sed 's#/var/log#/data/log#gw log_path.txt' app.conf
# 4. 提取日志中的IP(假设日志格式:2025-01-01 192.168.1.1 GET /)
echo "2025-01-01 192.168.1.1 GET /" | sed -E 's/.*(([0-9]{1,3}\.){3}[0-9]{1,3}).*/\1/'除了s替换命令,sed还有一系列单个字母的基础命令,配合地址定位可实现增删改查、行打印、行号显示等功能,简单易记,是处理文本的基础工具集,以下为核心常用命令,附速查和实战示例(均基于test.txt,加-i可原地修改文件):
d | [地址]d | /^$/d/sed/d:删除含sed的行 | |
p | [地址]p | -n '2,4p'-n '/hello/Ip':忽略大小写打印含hello的行 | |
a 文本 | [地址]a 文本 | 3a 6. add new line/sed/a >>>:含sed行后加>>> | |
i 文本 | [地址]i 文本 | 5i >>> test <<<$i last line before:最后一行前插入 | |
c 文本 | [地址]c 文本 | 5c 5. replace text/test/c test replace:替换含test的行 | |
= | [地址]= | -n '/sed/='-n '=;p':打印所有行的行号+内容 | |
y/源/目标/ | [地址]y/源字符/目标字符/ | y/12345/abcde/y/abc/ABC/:小写abc转大写ABC | |
n | [地址]n | n;d1~2n;d:删除奇数行 |
关键注意:
p命令必须配合-n使用,否则会重复打印内容;y命令的源字符和目标字符长度必须一致,仅支持单个字符的一一替换;d命令时需谨慎。如果仅做单行文本处理,掌握以上内容已足够;若要处理多行合并、文本反转、相邻行去重等复杂场景,需理解sed的双缓冲区机制——GNU sed通过两个缓冲区实现数据的临时存储和处理,这是所有进阶用法的核心。
sed全程基于行循环处理文本,维护两个缓冲区,职责明确:
即使是进阶用法,也基于此基础循环,只是对缓冲区的操作更复杂:
-n,打印模式空间内容,恢复末尾换行符;通过以下命令可实现两个缓冲区的复制、追加、交换,以及多行处理,这些命令是进阶用法的关键,无需死记硬背,理解作用即可,需要时直接套用示例:
h | |
H | |
g | |
G | |
x |
N | |
D | |
P |
基于双缓冲区机制,GNU sed支持多行文本处理和简单流程控制,适用于复杂文本场景,以下为核心场景和实现方式,附可直接套用的示例。
sed默认按单行处理,通过N/D/P配合缓冲区命令,可将多行加载到模式空间统一处理,最经典的场景为合并跨行文本。
示例:处理行尾以=结尾的软换行文本,合并为单行
# 1. 创建测试文件
cat > soft_line.txt << EOF
这是一=
行跨行了的=
文本,需要=
合并为单行
EOF
# 2. 合并命令
sed ':x ; /=$/ { N ; s/=\n//g ; bx }' soft_line.txt
# 输出:这是一行跨行了的文本,需要合并为单行命令解释::x定义分支标签,/=$/匹配行尾带=的行,N读取下一行到模式空间,s/=\n//g删除软换行,bx跳回标签x继续循环,直到无匹配行。
GNU sed支持通过标签+分支命令实现简单的流程控制,适用于多条件的复杂脚本,核心命令为标签定义和分支跳转,可配合s命令实现「条件替换」:
:标签:定义分支标签,无实际操作,仅作为跳转标记;b 标签:无条件跳转到指定标签,无标签则重启行循环;t 标签:有条件跳转,仅当本次循环中s命令替换成功时,跳转到指定标签;T 标签:有条件跳转,仅当本次循环中s命令未替换成功时,跳转到指定标签。核心特点:t/T与s命令强绑定,适合实现「匹配替换后继续处理」的逻辑。
融合GNU sed官方推荐用法和实际工作场景,分基础实用和进阶实战,覆盖配置文件处理、日志分析、文本格式化、批量操作等,直接抄作业即可。
# 1. 打印文件第5-10行(替代head/tail)
sed -n '5,10p' 文件名
# 2. 删除空行和以#开头的注释行(处理配置/日志)
sed '/^$/d; /^#/d' 文件名
# 3. 行首/行尾去空格/制表符(文本格式化)
sed 's/^[ \t]*//' # 行首去空格
sed 's/[ \t]*$//' # 行尾去空格
sed 's/^[ \t]*//; s/[ \t]*$//' # 同时去首尾空格
# 4. 批量注释/取消注释配置文件行
sed '3,8s/^/#/' 配置文件.conf # 注释3-8行
sed '/listen/s/^#//' 配置文件.conf # 仅取消含listen的行的注释
# 5. 原地替换并生成备份(安全修改文件)
sed -i.bak 's/abc/123/g' 文件名 # 替换后生成文件名.bak备份
# 6. 统计文件行数(替代wc -l)
sed -n '$=' 文件名
# 7. 所有行尾加分号(编程配置格式化)
sed 's/$/;/' 文件名
# 8. 给文件所有行加行号
sed = 文件名 | sed 'N;s/\n/ /'# 1. 文本去重(仅相邻行,替代uniq)
sed '$!N; /^\(.*\)\n\1$/!P; D' 文件名
# 2. 给文件首行添加版权头(GNU扩展0,首行前添加)
sed -i '0r copyright.txt' *.c # 从文件读取版权头内容
sed -i '1i/* Copyright (C) 2025 */' *.c # 直接添加文本
# 3. 反转文件行顺序(替代tac,基于双缓冲区)
sed -n '1!G; $p; h' 文件名
# 4. 批量重命名文件(配合ls/sh,将.txt改为.md)
ls *.txt | sed 's/\(.*\)\.txt/mv & \1.md/' | sh
# 5. 提取配置文件中的键值对(如key=value,仅打印value)
sed -E 's/^[a-zA-Z0-9_]+=(.*)/\1/' config.conf
# 6. 删除文件中最后一行
sed '$d' 文件名
# 7. 将文件中所有字母转为小写
sed 's/.*/\L&/' 文件名