
sed流编辑器小笔记(上)
sed流编辑器
概念
sed(Stream Editor,流编辑器),是一个非交互式的文本编辑工具。
- 可以处理文件、标准输入,也可以处理其他命令通过管道传来的内容
- 默认将处理后的内容输出到屏幕,不会修改原文件
- 可以只输出匹配到的行,也可以对内容进行替换、删除、插入等操作
- 配合
-i选项,还可以直接修改文件
如何理解
就像在编辑器中使用“查找与替换”,不过sed把这一步写成了命令,适合放进脚本中重复执行。
改一处可以手动点,改一百处就该让命令上班了。
原理
默认情况下,sed按下面的顺序逐行处理文本。
图中展示的是常见的逐行处理流程。-n关闭的是默认输出;命令中的p等显式输出仍然会执行。
读取
- 从文件或标准输入读取一行
- 存入临时缓冲区,称为模式空间(Pattern Space)
执行
- 命令依次作用于模式空间
- 指定地址后,命令只对被选中的行执行
显示
- 默认将当前模式空间输出到屏幕
- 再读取下一行,直到输入结束
💡提示
这是默认流程。
d、n、N等命令可以改变处理过程,模式空间也可以容纳多行内容。
使用说明
基本语法:sed [选项] '命令' [文件名 ...]
说明
- 选项:用于控制
sed的行为 - 命令:指定要执行的操作,通常放在单引号中
- 文件名:要处理的文件;不指定时读取标准输入
选项
| 选项 | 说明 |
|---|---|
-n |
关闭默认输出;p等显式输出命令仍然有效 |
-e |
指定一组编辑命令,可以重复使用 |
-i |
直接修改文件;添加备份后缀可保留原文件 |
-E |
使用扩展正则表达式 |
-r |
GNU sed中的扩展正则选项,本文统一使用-E |
-f |
从指定脚本文件读取sed命令 |
--help |
显示帮助,属于GNU sed选项 |
💡提示
示例以Linux中常见的GNU sed为主。macOS自带BSD sed,部分写法不同:
- 不备份直接修改:GNU sed使用
-i,BSD sed使用-i ''- 带备份修改:两者均可使用
-i.bak- 行前、行后插入文本:本文使用带反斜杠和实际换行的写法,便于两者使用
1~2p、替换标记2g及\U等属于GNU扩展
常用指令
| 指令 | 说明 |
|---|---|
s |
替换匹配到的字符串 |
d |
删除选中行,并开始下一轮处理 |
a\ |
在选中行后面追加文本 |
i\ |
在选中行前面插入文本 |
c\ |
将选中的行或行范围改成指定文本 |
y |
按位置转换字符,前后字符数必须相同,不使用正则表达式 |
p |
输出当前模式空间,通常配合-n使用 |
P |
只输出模式空间中第一个换行符之前的内容 |
q |
执行到该命令时退出;没有-n时仍会默认输出当前内容 |
l |
以便于观察的形式显示内容,转义不可见字符并标记行尾 |
! |
对地址没有选中的行执行后面的命令 |
= |
输出当前输入行的行号 |
w 文件 |
将选中的模式空间内容写入文件 |
r 文件 |
读取文件内容,在选中行后输出 |
补充
l不是大小写转换命令。GNU sed在替换文本中支持\L和\U,分别转换为小写和大写。
s/旧/新/g中的g是行内全局替换标记;单独的g命令则用于获取保持空间的内容。
地址范围
| 写法 | 说明 |
|---|---|
3p |
输出第3行 |
2,5d |
删除第2~5行,包含两端 |
$p |
输出最后一行 |
/shell/p |
输出包含shell的行 |
/start/,/end/p |
输出从匹配start到随后匹配end的行范围 |
/shell/!p |
输出不包含shell的行 |
这里的格式是地址 + 命令,例如3p,数字地址后不需要添加斜线。
正则表达式
默认使用基本正则表达式,加-E可以使用扩展正则表达式。
| 需求 | 基本正则 | 扩展正则 |
|---|---|---|
| 行首、行尾 | ^、$ |
^、$ |
| 任意单个字符 | . |
. |
| 前一个字符出现0次或多次 | * |
* |
| 数字字符 | [0-9]或[[:digit:]] |
相同 |
| 分组 | \(内容\) |
(内容) |
| 出现2~4次 | \{2,4\} |
{2,4} |
| 出现1次或多次 | [0-9][0-9]* |
[0-9]+ |
| 两个分支选择 | GNU sed支持反斜杠加竖线,属于扩展 | 直接用竖线分隔分支 |
举例
1 | printf '%s\n' 'shell 2023' | sed 's/[0-9]\{4\}/YEAR/' |
1 | shell YEAR |
基本用法
先创建一个名为
file.txt的文本文件,后面未说明时,均使用这份原始内容。
1 | cat > file.txt <<'EOF' |
1.替换文本中的字符串
将所有old_text替换为new_text。
1 | sed 's/old_text/new_text/g' file.txt |
1 | new_text new_text |
s表示替换g表示替换当前行内的所有匹配;不加时只替换每行第一个匹配- 这里是子串匹配,所以
old_text1中的old_text也会被替换
补充:从第N个匹配开始替换
1 | printf '%s\n' 'a a a a' | sed 's/a/A/2g' |
1 | a A A A |
2g在GNU sed中表示从每行第2个匹配开始替换,与第2行没有关系。
2.删除指定行
删除第2行
1 | sed '2d' file.txt |
1 | old_text old_text |
删除第3~5行
1 | sed '3,5d' file.txt |
1 | old_text old_text |
删除包含指定内容的行
1 | sed '/old_text/d' file.txt |
1 | hello shell |
扩展
1 | sed '/^$/d' file.txt # 删除完全空白的行,即空行 |
3.按条件替换
只替换行首的old_text。
1 | sed 's/^old_text/new_text/' file.txt |
1 | new_text old_text |
^表示行首,一个普通输入行只有一个行首,这里不需要再添加g。
如果想在包含shell的行中替换hello,可以把条件写在命令前面。
1 | sed '/shell/s/hello/hi/g' file.txt |
4.显示指定行
1 | sed -n '3p' file.txt |
1 | old_text1 old_text2 |
-n关闭默认输出3p只输出第3行- 不加
-n时,所有行仍然默认输出,第3行还会被p额外输出一次。不是终端卡出了重影,是这一行领了两次“打印任务”
5.组合多个表达式
1 | sed -e 's/old_text1/new_text1/g' -e 's/old_text2/new_text2/g' -e '5d' file.txt |
1 | old_text old_text |
多个命令会按顺序执行,可以重复使用-e,也可以用分号分隔。
1 | sed 's/old_text1/new_text1/g;s/old_text2/new_text2/g;5d' file.txt |
6.后向引用
匹配给定样式中的一部分,再在替换内容中引用它。
将digit 8替换为8
1 | echo 'this is digit 8 in a number' | sed 's/digit \([0-9]\)/\1/' |
1 | this is 8 in a number |
\([0-9]\)是第一个分组\1引用这个分组匹配到的内容
交换两组内容的位置
1 | echo 'aaa BBB' | sed -E 's/([a-z]+) ([A-Z]+)/\2 \1/' |
1 | BBB aaa |
分组从左到右编号,\1对应小写字母,\2对应大写字母。
小结
(1)sed默认把结果输出到屏幕,不修改原文件
(2)地址用于选择行,命令用于执行具体操作
(3)-n和p通常配合使用;s/旧/新/g用于行内全局替换
(4)基本正则和扩展正则的分组写法不同,但都可以通过\1等引用分组内容








