29

"Sed" 高级功能:你需要一个人肉状态机

 4 years ago
source link: https://www.tuicool.com/articles/rqieQvb
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

sed命令有两个空间,一个叫pattern space,一个叫hold space。这两个空间能够证明人类的脑瓜容量是非常小的,需要经过大量的训练和烧脑的理解,才能适应一些非常简单的操作。

不信看下面简单的命令,作用是,删除文件中最后两行。

sed 'N; $!P;$!D;$d' file

《Linux生产环境上,最常用的一套“Sed“技巧》 一文中,我们介绍了常用的sed命令和操作,而且使用了两张图来作为辅助。但可惜的是,这两张图,严格来说是不准确的 (比如s命令,只是其中的一个子集),即使它能够帮助初学者快速入门。

本篇属于sed的中级用法,常见在一些sed脚本中,在日常中应用并不多,但往往能够获得意想不到的效果。

工作模式

这要从sed的工作模式来说起。

qqieayU.png!web 按照我们读取一个文件的尿性,一般是持续循环读取。比如下面的python代码,print代表p命令。

with open('file', 'r') as f:
    for line in f.readlines():
        print(line)

sed命令在这之上,还缓冲了另外一个东西。那就是“上一行的内容” ,叫做hold space。而当前行,叫做patter space。用python简单表现一下:

hold_space = ""
with open('file', 'r') as f:
    for pattern_space in f.readlines():
        print(hold_space,pattern_space)
        hold_space = pattern_space

具体过程,大体与上面的代码类似,以上面的图为例。在这个例子中,hold space不参与运算,是全程无感的:

1、读取当前行 wtf.. 到 Pattern space

2、执行命令 p ,这会打印出当前行

3、把Pattern space的内容,赋值给Hold space

4、继续下一行的处理,循环这个过程

一个例子:x

但我想稍微操作一下这两个缓冲区。这个操作就是swap,使用x表示,这也是一些文本编辑器的一贯尿性。

也就是,在p之前,我们加上了个x。表示先将这两个缓冲区进行置换,然后再往下走。

J3uuIzq.png!web

hold_space = ""
with open('file', 'r') as f:
    for pattern_space in f.readlines():
        swap(hold_space,pattern_space)
        print(hold_space,pattern_space)
        hold_space = pattern_space

让我我们来想象一下这个过程。

1、刚开始,hold_space的内容是空。然鹅,还没被填充,它就被使用了,和当前行进行了置换

2、p命令用在了置换后的缓冲区上,第一次打印出了空行,fuck

3、继续嘟嘟嘟,现在到了最后一行,马上进行了置换,没机会打印就到了hold_space中了

4、当前行,存放的是倒数第二行的数据,最后一行见光死,就永远没有机会面世了

我们当然有办法把它搞出来,比如,我执行偶数次的交换x。

sed -n 'x;x;x;x;p' file

有木有一股骑着羊驼走天下的的感觉?

举个栗子

你可能会想,我对这两个缓冲区交换,有什么用?接下来看这个文件。

小姐姐味道公众号
CEO
加菲猫经理
IT Manager
系统毁灭师
Sysadmin
小哥哥味道
Developer
爱卖东西的经理
Sales Manager
风清扬
Dog

文件奇数行是名称,偶数行是职位。我们想要输出所有 Manager 的名字。就可以使用下面的命令。

sed -n -e 'x;n' -e '/Manager/{x;p}' file

命令分为两个部分。

x;n 表示将偶数行保存在pattern space,那么奇数行就保存在hold space中。

/Manager/{x;p} 命令将在pattern space上执行对Manager关键字的查找。如果符合条件,则再次交换p和h缓冲区,输出奇数行对应的名字。

上面的 xn ,就是针对这两个缓冲区的命令。这样的命令有很多。

这些命令,如果多了,可以使用{}包围起来,就像上面的命令一样。这些命令的位置与我们上一篇所说的,在同一个地方。

常用的:

x请容许我用英文装个b:Exchange the contents of the hold and pattern spaces.

d清空当前的pattern space,然后进入下一个循环

D删除pattern space的第一行(multiline pattern)

h复制pattern space到hold space

H追加pattern spaced到hold space

g复制hold space到pattern space

G追加hold space到pattern space

n读取下一个输入行到pattern space

N追加下一个输入行到pattern space,同时将两行看做一行,但是两行之间依然含有n换行符

p打印当前的pattern space

P打印当前的pattern space中的第一行

不常用的

上次提到的推箱子游戏,就用了很多这种东西。为了使使用者在书写sed脚本的时候真正的"自由",sed还允许在脚本中用":"设置记号。标签,有种类似编程语言的特性了。

q退出sed,可以增加执行速度

l列出当前行,包含不可打印字符

l width列出当前行,使用一个 width characters 结尾

b label跳到相应的标签,分之命令。

t labelif分支,从最后一行开始,条件一旦满足或者T,t命令,将导致分支到带有标号的命令处,或者到脚本的末尾。测试命令。

T label错误分支,从最后一行开始,一旦发生错误或者T,t命令,将导致分支到带有标号的命令处,或者到脚本的末尾。

当然还有其他更不常用的,可以使用man命令查看

man sed

一些命令:开启训练模式

看着一行行进行处理,好像很简单是不是?不可能的,看下面几个简单的命令,训练一下生锈的脑子。

一个流水线一样的命令

sed -n '2{h;n;x;H;x};p' file

交换第2行和第3行的内容

输出最后一行

sed 'N;D' file

输出文件中最后两行

sed '$!N; $!D' file

删除文件中最后两行

sed 'N; $!P;$!D;$d' file

打印偶数行的另一种写法

sed –n 'n;p' file

每隔5行加入一个空行。

sed 'n;n;n;n;G' file

输出含AAA和BBB和CCC(任意顺序)的段落

sed -e '/./{H; $!d;}' -e 'x;/AAA/!d; /BBB/!d; /CCC/!d' file

颠倒行序(使末行变首行,首行变末行)

sed -n '1!G; h; $p' file

这个命令有点绕,首先, 1!G 对除了第一行的其他行进行了G操作,然后反向复制回去,到了最后一行,就直接打印Pattern Space。 $ 表示到了最后一行执行下面的命令,也可以是 ${p}

一个带标签的用法

sed -e :a -e '$q;N;11,$D;ba'

打印最后10行。 a 是标签。语法就是单独的行,使用 : 分隔。

为了提高你在公司的竞争力,你是可以弄一堆sed脚本唬人(埋雷)的。和某些perl脚本一样,即使是有相关经验的开发着,理解起来也要下点功夫,就不要说其他人了。

这就是sed,简约但不简单的命令,本文算是一个中级入门。中级入门也有点烧脑,因为你的脑瓜里,需要一直维护着这两个缓冲区。又是置换,又是清空,相当于人肉状态机。当然,怎么把这个过程讲的尽量简单一点,还是浪费了作者不少脑细胞的。哪怕你点个赞,也是延缓小姐姐走向老年痴呆时间的一个途径。有了你的支持,小姐姐也可以想点技术之外的事情,比如喷喷bat什么的。

JZr2a2v.png!web

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK