sed 进阶使用
工作原理
sed 维护两个数据缓冲区: 活动模式空间 和 辅助保持空间
-
两者最初都是空的
-
sed 通过对每一行输入执行以下循环进行操作
- 从输入流中读取一行,删除任何尾随的换行符,并将其放置在 模式空间 中
- 然后执行命令,每个命令都可以有一个与之相关的 地址 【地址是一种条件代码,只有在执行命令之前验证了条件,才能执行命令】
- 当到达脚本末尾时,除非使用 -n 选项,否则模式空间的内容将打印到输出流中
- 然后,下一个循环将为下一个输入行开始
-
除非使用 特殊命令,否则将在两个循环之间删除 模式空间
-
另一方面,保持空间 在周期之间保持其数据
地址
按数字选择行
number commands
指定行号 number 将仅匹配输入中的该行,比如:seq 6 | sed '3d'
删除第 3 行first~step commands
从第 first 开始每隔 step 行进行匹配,可以计算 first + ( n * step ) 行的公式,其中 n 是周期,比如:seq 6 | sed '1~2d'
删除 1, 3, 5 行$
默认表示最后一行,但可以使用命令选项进行更改
文本匹配选行
默认的正则表达式是 BRE,通过 -E 或者 -r 选项可用 ERE
-
形式
/regexp/
如果正则表达式本身包含/
字符需要进行转义seq 6 | sed '\~3~d'
-
正则表达式界限符
/
可以用任何其他单个字符代替,注意转义问题,比如\%regexp%
-
正则表达式匹配修饰符 是一个 GNU 扩展
/regexp/I
- I: 用于不区分大小写的正则表达式匹配
- M: 以多行模式匹配正则表达式
范围地址
通过指定用 逗号 分隔的两个地址来指定地址范围
-
n,m commands
表示第 n 行到 m 行,如果 m < n 仅匹配 n 行 -
number,/regexp/ commands
从 number 行开始到第一个匹配正则表达式的行结束:一个范围将始终跨越至少两行(输入流结束除外) -
/regexp/,number commands
类似上面,第一个匹配正则表达式的行开始到 number 行结束(输入流结束除外) -
/regexp/,/regexp/ commands
第一个正则表达式匹配的第一个目标行开始到第二个正则表达式匹配的第一个目标行结束 -
GNU 扩展
0,/regexp/
其中 0 会将尝试在第一输入行中匹配正则表达式,效果是正则表达式可以匹配第一行的内容number,+N
在 number 行到 number+N 行number,~N
从 number 行开始到 N 的倍数行结束
多行技术
可以使用 (D, G, H, N, P) 将多行作为一个缓冲区进行处理,它们与小写的对应项 (d, g, h, n, p) 相似,只是这些命令附加或减去数据同时考虑嵌入的换行符,允许从模式中添加和删除行并保留空格
- D: 从 模式空间 中删除行,直到第一行换行,然后重新开始循环
- G: 将 保留空间 中的行附加到 模式空间 ,并在其前面添加换行符
- H: 将 模式空间 中的行附加到 保留空间 ,并在其前面添加换行符
- N: 将输入文件中的行附加到 模式空间
- P: 从 模式空间 打印行,直到第一行换行
(D, G, H, N, P) 用于多行,(d, g, h, n, p) 用于单行
例子:
$ seq 6
1
2
3
4
5
6
$ seq 6 | sed -n 'N;l;D'
1\n2$
2\n3$
3\n4$
4\n5$
5\n6$
- 首先将第一行读入模式空间 ;;; 此时模式空间:(1)
- 在每个循环开始时,N命令将 换行 和 下一行 附加到 模式空间 ;;; 此时模式空间:(1\n2)
- l 命令明确地 打印模式空间的内容,此命令在打印时会额外附带一个
$
表示行末,输出1\n2$
;;; 此时模式空间:(1\n2) - 然后,D 命令删除 模式空间 的内容,直到第一行换行,然后重新开始循环 ;;; 此时模式空间:(2)
- 在下一个循环中,N 命令将换行符和下一个输入行附加到 模式空间 ;;; 此时模式空间:(2\n3)
- 以此类推
处理段落等文本块(而不是逐行)的常用技术是使用以下结构
sed '/./{H;$!d} ; x ; s/REGEXP/REPLACEMENT/'
/./{H;$!d}
对所有非空行进行操作,并将当前行(在模式空间中)添加到保持空间,在除最后一行之外的所有行中,模式空间都被删除并重新开始循环- x 命令将累积的行从保持空间取回模式空间
- s 命令然后对段落中的所有文本(包括嵌入的换行符)进行操作
分支和流量控制
默认情况下
- sed 将输入行读入模式缓冲区
- 然后继续按顺序处理所有命令
- 没有地址的命令会影响所有行,带地址的命令只影响匹配的行
一些命令可以用作条件或更改默认流控制
- d 删除(清除)当前模式空间,并重新启动程序循环而不处理其余命令并且不打印模式空间
- D 删除模式空间的内容直到第一个换行符,并重新启动程序循环而不处理其余命令并且不打印模式空间
- 地址和正则表达式可用作 if/then 条件
- b 无条件分支(即:始终跳转到标签,跳过或重复其他命令,而不重新启动新循环):结合地址,分支可以在匹配的行上有条件地执行
- t 只有在读取最后一个输入行或执行另一个条件分支后,命令成功时,才有条件地分支(即:跳转到标签)
- T 类似但与 t 命令相反:仅当自读取最后一个输入行以来没有成功的替换时才分支
b, t, T 命令后面可以跟一个 标签(通常是一个字母)
-
标签定义为冒号后跟一个或多个字母
-
如果省略了标签,分支命令将重新启动循环
-
注意分支到标签和重新启动循环之间的区别:
- 当循环重新启动时,sed 首先打印模式空间的当前内容,然后将下一个输入行读入模式空间
- 跳转到标签(即使它在程序的开头)不会打印模式空间,也不会读取下一个输入行
构成循环
# 死循环
seq 3 | sed ':x ; bx'
解除死循环,通常由 n 或 N 个命令补充:
- 两个命令都将下一个输入行读入模式空间,而无需等待循环重新启动
- 在读取下一个输入行之前,n 打印当前模式空间,然后将其清空,而 N 则在模式空间中添加一个新行和下一输入行