sed流编辑器

概念

sed(Stream Editor,流编辑器),是一个非交互式的文本编辑工具。

  • 可以处理文件、标准输入,也可以处理其他命令通过管道传来的内容
  • 默认将处理后的内容输出到屏幕,不会修改原文件
  • 可以只输出匹配到的行,也可以对内容进行替换、删除、插入等操作
  • 配合-i选项,还可以直接修改文件

如何理解

就像在编辑器中使用“查找与替换”,不过sed把这一步写成了命令,适合放进脚本中重复执行。

改一处可以手动点,改一百处就该让命令上班了。

原理

默认情况下,sed按下面的顺序逐行处理文本。

flowchart TD A["文件 / 管道 / 标准输入"] --> B{"还有下一行?"} B -->|有| C["读取一行,放入模式空间"] C --> D["按顺序执行匹配地址的命令"] D --> E{"是否由 d 提前结束本轮?"} E -->|是| B E -->|否| F{"开启默认输出?"} F -->|是| G["输出当前模式空间"] F -->|"否:使用 -n"| B G --> B B -->|没有| H["处理结束"]

图中展示的是常见的逐行处理流程。-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
2
printf '%s\n' 'shell 2023' | sed 's/[0-9]\{4\}/YEAR/'
printf '%s\n' 'shell 2023' | sed -E 's/[0-9]{4}/YEAR/'
1
2
shell YEAR
shell YEAR

基本用法

先创建一个名为file.txt的文本文件,后面未说明时,均使用这份原始内容。

1
2
3
4
5
6
7
cat > file.txt <<'EOF'
old_text old_text
hello shell
old_text1 old_text2
123456 abcde
old_text at end
EOF

1.替换文本中的字符串

将所有old_text替换为new_text。

1
sed 's/old_text/new_text/g' file.txt
1
2
3
4
5
new_text new_text
hello shell
new_text1 new_text2
123456 abcde
new_text at end
  • 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
2
3
4
old_text old_text
old_text1 old_text2
123456 abcde
old_text at end

删除第3~5行

1
sed '3,5d' file.txt
1
2
old_text old_text
hello shell

删除包含指定内容的行

1
sed '/old_text/d' file.txt
1
2
hello shell
123456 abcde

扩展

1
2
3
sed '/^$/d' file.txt                # 删除完全空白的行,即空行
sed '/^[[:space:]]*$/d' file.txt # 同时删除只有空格、制表符等空白字符的行
sed '$d' file.txt # 删除最后一行

3.按条件替换

只替换行首的old_text。

1
sed 's/^old_text/new_text/' file.txt
1
2
3
4
5
new_text old_text
hello shell
new_text1 old_text2
123456 abcde
new_text at end

^表示行首,一个普通输入行只有一个行首,这里不需要再添加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
2
3
4
old_text old_text
hello shell
new_text1 new_text2
123456 abcde

多个命令会按顺序执行,可以重复使用-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等引用分组内容

参考:GNU sed命令说明、GNU sed处理流程。