sed总结
这篇博文是在阅读sed&awk第二版这本书做的记录,仅作为以后备忘使用.有些仅仅是自己的理解,不保证绝对正确.本文假设读者已经掌握基本的正则表达式
-
名词解释
模式空间:sed维护的一个缓冲区,脚本或命令都是直接作用与这个缓冲区.按照sed的工作方式分类,模式空间还分为单行模式空间及多行模式空间.
保持空间:也是一个缓冲区,他可以与模式空间交换数据. - sed入门
sed基本语法:
[address]command
基本上sed语法都是在地址后面跟命令.有些命令只接受单地址,但是有些命令接受一个范围的地址.
地址可以用正则表达式匹配(/^$/d),也可以用行号表示(1,5d),甚至可以用特殊标记(1,$d)($在这里表示最后一行).
花括号可以在同一个地址上执行多条命令,形如
address{
command1
command2
command3
}
花括号是可以嵌套在一个地址中,也即是在一个范围里面进行一些操作.例如:/#ifdef DEBUG/,/#endif/{
/^$/d
}
删除上述范围的所有空行
! 表示不对某一个地址执行某执行,比如 $!n表示不在处理文本的最后一行执行n操作.这里的$表示处理文本的最后一行(通常是文件)
常用的命令介绍:
替换 s
[address]s/pattern/replacement/flage
flage选项
地址需要(/)作为定界符,而正则表示式可以使用任何符号作界定符.例如:s!/usr/bin!/usr2/bin!
n,它的取值是1到512.意味对模式的第n次匹配进行替换
g,它意味对所有匹配到的模式均进行替换.如果不添加这个选项,它将只替换第一次匹配
p,打印模式空间的内容
W file,将模式空间的内容写入到文件file
flage是可以组合使用的只要它有意义,
replacement部分(&)号表示正则表达式匹配的值,(\n)表示第n个匹配的子串.其中0子串表示整个匹配到的子串.与标记(&)相当
删除 d
它可以作用于一个地址和一个范围,例如:sed '1,$d' test1将删除从第一行到最后一行的所有内容.
插入,更改,追加
[line-address]i\
text
[address]c\
text
[line-address]a\
text
上诉三条语法更改可以应用在一个范围,而其余两条都只应用于一个地址.插入对应在模式空间所对应当前行的前面,改变对应当前行,追加对应后面.插入和追加不影响模式空间的内容,新加的内容不会匹配后面的脚本
列表 l
输出模式空间的内容
转换 [address]y/abc/xyz
它将地址匹配的行中出现的a换成x,出现的b换成y,依次类推
打印 p
打印模式空间的内容
打印行号 =
[address]=
读新的一行 n
它是单行模式空间的命令,它读一行数据覆盖模式空间.但不改变执行流程.(sed&awk)相关部分写的晦涩难懂,坑啊
-
sed进阶
多行模式空间
N,D,P
N是多行模式空间的n,它将新的一行追加到模式空间的尾上,在原来的行尾增加标记'\n'符.
D是多行模式空间的d,它只删除模式空间的开始到第一个'\n'的位置的文本
P是多行模式空间的p,它只打印模式空间的开始带第一个'\n'的位置的文本
保持空间
h,H 他们分别表示将模式空间的内容复制到报纸空间和追加到保持空间.操作完成之后追加一个换行符
g,G 他们分别表示将保持空间的内容复制到模式空间和追加到模式空间.在操作之前先写入一个换行符
x 交换保持空间和模式空间的数据
无条件跳转
[address]b label
在替换命令之后可以使用有条件跳转,仅在操作成功时才跳转
[address]t label
定义一个标签
: label
-
关于控制流的问题
这是一个非常容易造成迷惑的地方,以下详细分析一下n,N,d,D的异同,不知是理解的问题还是其他问题.对于书的描述越看越迷惑.以下根据我的疑问作出实验得出答案.
- 使用n命令之前模式空间的变化,以及控制流的走向?(p命令不改变模式空间内容及控制走向)
图1
图2
分析:图1说明3行文本都匹配到正则表达式里面.图2显示出了3行文本,第一条显示是第一个l命令显示的,接着执行了n命令,接着执行p命令显示了第二条,然后控制返回了顶端,脚本携带第三条记录被显示出来,到n的时候已文件已结束
结论:n命令不改变执行流程,只是用新的东西覆盖原有的东西.但是sed&awk第二版书相关章节说控制返回脚本顶端实在是理解不了.若控制返回了顶端第二条记录尾上应该还有个$符 - 使用N命令之前模式空间的变化以及控制流的走向?
图3
图4
分析:图3第一行显示是脚本中的l显示的,第二行和第三行是N命令之后p输出的.最后一个记录是第二次脚本执行l输出的.图4第一行是l显示的,第二行是第二个l显示的,第三行是第二次执行脚本的时候第一个l显示的
结论:N只是向原有模式空间追加换行和新一行的内容.不改变控制流走向. - 使用d命令之前模式的模式空间的变化以及控制流走向.
图5
分析:若控制流不返回脚本顶端,第一个l会输出图中的第一个记录,d后面的命令会执行.也就是说最后会生成3条记录.实际上只有两条记录.
总结:d命令会清空模式空间所有内容并且控制会返回脚本顶端.它还会自动向模式空间载入新的内容. - 假设D命令只删除第一行且控制直接跳转到脚本顶端,但是不会主动向模式空间载入新的内容,且不重置模式空间的内容.
结论:D命令之后控制直接返回至脚本顶端,原模式空间的内容不重置.若D之后模式空间为空,sed自动读下一行重复执行脚本.
- 使用n命令之前模式空间的变化,以及控制流的走向?(p命令不改变模式空间内容及控制走向)
- 最后,对sed作以下总结:
- sed的内部计数器(行号)只增加不减少,而增加的方法有两种,模式空间在命令d或D清空的时候,sed会自动读新的一行进来从而增加内部计数器,第二种是在n或N的作用下.内部计数器的值始终和模式空间最后一行所对应的行号一致.
- D或d命令操作之后,控制流;立即回到脚本顶端.也就是说他们后面的代码不会执行,如果D命令之后模式空间不为空,模式空间的内部不会重置.
- 在某一个地址上应用多条命令,也就是在某一个地址后面用花括号执行多条命令.执行的命令D或d,控制会回到脚本顶端,
- 最终,根据我的理解如果我要自己实现一个sed我会这样做
- 定义基本数据结构
定义处理文本的内部计数器也即行号,定义缓冲区1(模式空间),定义缓冲区2(保持空间), - 定义一组用户可操作的命令接口
l 输出缓冲区1(模式空间)的所有字符,不删除末尾的$符
p 打印缓冲区1(模式空间)的所有内容,直接删除缓冲区末尾的$符后用C语言的snprintf,也就是说换行符也会输出
n 从处理文本中取新的一行数据覆盖缓冲区1(模式空间),内部计数器加1
d 清空模式空间的所有数据,控制转入脚本的第一行(不是处理文本)
N 在模式空间替换末尾的$符为换行符追加新一行的内容后再在新内容追加一个$符,内部计数器+1
P 显示从首行开始一直到第一个嵌入的换行符的所有内容
D 删除从首行开始一直到第一个嵌入的换行符的所有内容,控制转入脚本的第一行
i,c,a 插入,改变,追加没啥特殊的地方,他们不会引起内部计数器的变化.也就是说你插入了追加了新内容,它不会被接下来的处理流程命中.他们分别作用域当前处理行的前一行,当前处理行,当前处理行后一行
s 替换,可选的地址范围限定,替换....,也没啥可特别注意的地方.有条件的跳转指定t作用与替换操作之后
b 可选的地址限定,跳转到脚本的某一行
t 可选的地址选项,上一条替换操作成功才执行跳转
: 声明一个标签
g,G 从缓冲区2(保持空间)复制或追加数据到缓冲区1(模式空间),追加(复制不算)之前先向模式空间追加一个换行符
h,H 从缓冲区1(模式空间)复制或追加数据到缓冲区2(保持空间),,追加(复制不算)之前先向保持空间追加一个换行符
x 交换两个缓冲区的数据
q 退出处理流程
- sed代码操作流程
- 初始化sed内部数据结构(内部计数器等)
- 处理文本已经处理完,执行命令q退出处理流程
- 如果模式空间为空,sed自动执行命令n
- 执行用户的脚本
- 若用户的命令中未出现D或d命令,用户代码执行完成后跳转到过程2
- 若用户的命令脚本中出现D或d命令,D或d操作执行后直接跳转到过程2.D或d后面的代码将没有机会执行
- 若用户的命令出现了b或t(无)条件跳转指令,执行流程将跳转到用户代码的某一行(用户指定的)
- 其余的命令都是顺序执行
- 注意,内部计数器与模式空间的最后一行数据所对应的当前行的行号一致.也就是说即使是多行模式下,执行D命令之后,控制返回到上面的过程2.由于模式空间不为空,sed不自动执行n命令.所以,内部计数器不变.换句话说,在控制返回之前和返回之后内部计数器都不变.
- 定义基本数据结构
-