保姆猴讲讲sed的模式空间和暂存空间
先理解 [模式空间] 和 [暂存空间]
sed 默认处理的就是被逐行读取的模式空间的行,所以不会更改原文件。
这里的伪代码只做参考,不代表其逻辑是这样实现的。
//再来个细致的伪代码
/**
* file 文件
* patternList 执行的多个脚本 -e pattern1 -e pattern2 ...
*/
sed(file, patternList) {
patternSpace.init(); //模式空间
holdSpace.init(); //暂存空间
lines = file.getLines(); //获取行
while (lines.hasNext()) { //有下一行,则继续
line = lines.next();
patternSpace.add(line); //添加一行
operate(patternSpace, patterns);//对模式空间内的行进行处理
patternSpace.printAll();//打印处理后的内容
patternSpace.clear();//清空模式空间
}
}
小猴们请看:完全没有用到暂存空间
sed只需要模式空间就可以完成操作,而暂存空间只是给了方便用户完成一些复杂或者说不可能操作的一个空间。
比如,小猴要把两个瓶子里的水交换,那么就需要另一个瓶子才能做到,暂存空间就是这样用的。
类似于玩数据结构,用list实现一个栈或者队列。
命令 | 作用 |
---|---|
n | 打印模式空间当前行,读取下一行到模式空间,执行脚本中的操作。 |
N | 读入下一行,追加到模式空间行后面,此时模式空间有两行,脚本的操作对两行都有效。 |
h | 把模式空间里的所有行复制到暂存空间。(覆盖暂存空间原来的) |
H | 把模式空间里的所有行追加到暂存空间。 |
g | 用暂存空间的所有内容替换模式空间的行。 |
G | 把暂存空间的所有内容追加到模式空间的行后。 |
x | 将暂存空间的所有内容和模式空间里的当前行互换。 |
样例文件web
add1=www.monkey.com
add2=http://127.0.0.1:80
add3=https://monkeys.cn
id=1
name=张麻子
n
# 来看看n,n本身也是一个脚本命令,一般用分号或者-e与其他命令隔开
# sed 'n;d' web 隔行删除
[monkey@osp~01] /home/monkey> sed 'n;d' web
add1=www.monkey.com
add3=https://monkeys.cn
name=张麻子
原理
//sed 'n;d' web
// patternList.get(0) = n;
// patternList.get(1) = d;
n (patternSpace, lines) {
patternSpace.printAll(); //打印所有
patternSpace.clear(); //清空
patternSpace.add(lines.next()); //下一行写入模式空间
if (patternList.hasNext())
patternList.next().run(); //执行下一个pattern
//这里假设是java面向对象,参数都是对象,所以调用者作为参数的patternSpace,lines的值被改变了。
}
d () {
delete(); //删除行
if (patternList.hasNext())
patternList.next().run(); //执行下一个pattern
}
/**
* 所以,当第1行进入模式空间开始执行时,遇到n (第1行被直接输出,第2行写入),n执行完毕,执行d,删除第2行
* 第3行按原进度进入模式空间开始执行,遇到n...
* ...
*/
N
这个更难理解了,看结果先
[monkey@osp~01] /home/monkey> sed 'N;d' web
name=张麻子
# 再写入一行,此时里边6行内容
[monkey@osp~01] /home/monkey> echo age=18 >> web
[monkey@osp~01] /home/monkey> cat web
add1=www.monkey.com
add2=http://127.0.0.1:80
add3=https://monkeys.cn
id=1
name=张麻子
age=18
# 执行结果为空
[monkey@osp~01] /home/monkey> sed 'N;d' web
[monkey@osp~01] /home/monkey>
//sed 'N;d' web
// patternList.get(0) = N;
// patternList.get(1) = d;
N (patternSpace, lines) {
if (!lines.hasNext()) {
return; //如果下一行不存在了,就直接停止执行。
}
patternSpace.add(lines.next()); //下一行写入模式空间
if (patternList.hasNext())
patternList.next().run(); //执行下一个pattern
//这里假设是java面向对象,参数都是对象,所以调用者作为参数的patternSpace,lines的值被改变了。
}
d () {
delete(); //删除行
if (patternList.hasNext())
patternList.next().run(); //执行下一个pattern
}
/**
* 第1行进入,执行N (把第2行强行加入),执行d(删除行),删掉2行,打印结果:都是空
* 第3行,执行N(第4行强行加入),执行d(删除行),打印结果,都是空
* 第5行执行N,因为没有第6行,提前return了,后边的d就没执行,打印:第5行。
* 所以偶数行都会被删除,奇数行会有最后一行因为提前中止不被删除。
*/
N,n都学了,看个复杂例子
[monkey@osp~01] /home/monkey> sed 'N;n;s/=/:/g' web
add1=www.monkey.com
add2=http://127.0.0.1:80
add3:https://monkeys.cn
id=1
name=张麻子
age:18
# N 把下一行读入,此时有两行,都是=,还没执行替换处理,下一条命令
# n 打印所有(2行),清空,读入第3行,下一条命令
# s 替换= 为:无下一条命令,打印,清空,读入..
# 所以前三条输出为 = = :
# 读入第4行, N...
# 一次类推,每三个一循环...
h
// 模式空间所有内容复制到暂存空间,覆盖暂存空间内容
h (patternSpace, holdSpace) {
holdSpace.clear(); //清空暂存
holdSpace.addAll(patternSpace.getAll()); //复制所有模式空间内容到暂存
if (patternList.hasNext())
patternList.next().run(); //执行下一个pattern
}
// h只是复制到暂存空间,对模式空间内容不影响,如果后续没有再转回来的操作,则不影响原来的结果。
h,H,g,G,x 都是同理,参考h就能理解。
这里写一个简单例子
[monkey@osp~01] /home/monkey> sed 'h;H;g;G;x;s/=/:/g' web
add1:www.monkey.com
add1:www.monkey.com
add2:http://127.0.0.1:80
add2:http://127.0.0.1:80
add3:https://monkeys.cn
add3:https://monkeys.cn
id:1
id:1
name:张麻子
name:张麻子
age:18
age:18
拿出一张纸,画两个筐…
操作 | 模式空间 | 暂存空间 |
---|---|---|
>>>>第一行<<<< | add1=www.monkey.com | null |
h 模式复制到暂存(覆盖) | add1=www.monkey.com | add1=www.monkey.com |
H 模式追加到暂存 | add1=www.monkey.com | add1=www.monkey.com add1=www.monkey.com |
g 暂存替换到模式 | add1=www.monkey.com add1=www.monkey.com | add1=www.monkey.com add1=www.monkey.com |
G 暂存追加到模式 | add1=www.monkey.com add1=www.monkey.com add1=www.monkey.com add1=www.monkey.com | add1=www.monkey.com add1=www.monkey.com |
x 交换模式和暂存 | add1=www.monkey.com add1=www.monkey.com | add1=www.monkey.com add1=www.monkey.com add1=www.monkey.com add1=www.monkey.com |
替换=为: | add1:www.monkey.com add1:www.monkey.com | add1=www.monkey.com add1=www.monkey.com add1=www.monkey.com add1=www.monkey.com |
打印全部,清空模式 打印内容为: add1:www.monkey.com add1:www.monkey.com | null | add1=www.monkey.com add1=www.monkey.com add1=www.monkey.com add1=www.monkey.com |
>>>>第二行<<<< | add2:http://127.0.0.1:80 | add1=www.monkey.com add1=www.monkey.com add1=www.monkey.com add1=www.monkey.com |
h 模式复制到暂存(覆盖) | add2:http://127.0.0.1:80 | add2:http://127.0.0.1:80 |
… | … | … |
再来一个几乎每个讲模式空间的文章都有的,但是劝退新手的例子:
反转文件内容 sed ‘1!G;h;$!d’ web
这个需要了解一下sed的匹配语法
文章链接:
[保姆猴🐒详解sed]
# 给个样子回忆一下,如果回忆不起来或者没学过,请看上边文章链接
# 前边的匹配项不写则默认全局修改
sed [选项] '/匹配项/操作 操作内容' 文件
##包含http的行前插入123
sed -i '/http/i 123' web
sed [选项] '行匹配项 操作 操作内容'
##行号为2的行删除
sed '2d' web
例子执行结果:
# '1!G;h;$!d'
[monkey@osp~01] /home/monkey> cat web
add1=www.monkey.com
add2=http://127.0.0.1:80
add3=https://monkeys.cn
[monkey@osp~01] /home/monkey> sed '1!G;h;$!d' web
add3=https://monkeys.cn
add2=http://127.0.0.1:80
add1=www.monkey.com
解析一下,拿个纸,画俩筐…
操作 | 模式 | 暂存 | 备注 |
---|---|---|---|
>>第一行<< | add1… | null | |
1!G 操作是G,匹配行设定了1! 非行号为1 即除了第一行,其他行执行G | add1… | null | 行号为1,不执行G |
h 复制模式到暂存 | add1… | add1… | |
!
d
<
b
r
/
>
操作是
d
删除,匹配行设定
!d<br />操作是d删除,匹配行设定
!d<br/>操作是d删除,匹配行设定! 非最后一行 即除了最后一行,都执行删除 | null | add1… | 不是最后一行,执行删除 |
打印,清空 | null | add1… | 打印值为空 |
>>第二行<< | add2… | add1… | |
1!G | add2… add1… | add1… | 行号为2,执行G 暂存追加到模式 |
h | add2… add1… | add2… add1… | |
$1d | null | add2… add1… | 不是最后一行,执行删除 |
打印,清空 | null | add2… add1… | 打印值为空 |
>>第三行<< | add3… | add2… add1… | |
1!G | add3… add2… add1… | add2… add1… | 行号为3,执行G |
h | add3… add2… add1… | add3… add2… add1… | |
$!d | add3… add2… add1… | add3… add2… add1… | 最后一行,不执行删除 |
打印,清空 | null | add3… add2… add1… | 打印结果: add3… add2… add1… |
有需要详解的命令或者想了解的知识或者本文有描述不准确的地方可以评论或者私信