06、sed 循环语句

循环 其实就是一种重复,在满足指定的条件下,重复的做某些事情。就好比如只要时间没到 18:30,那么我们一直在重复的上班。

和其它语言一样,sed 也有循环语句,也有条件判断分支语句。

只是sed 的循环语句真的很反人类,难记又难懂。

本章节我们就来揭开着难记又难懂的语法。

sed中的循环语句没有 for 、loop、while 这些关键字,而是和 C 语言 语句有点像。

sed也有标签 ( label ) 这个概念,而且可以设置标签。

sed的循环,就是不断跳转到标签标记的行并继续执行其余命令来实现的。

sed语言使用 冒号(:) 来实现标签名。

sed中设置 标签 ( label ) 的语法如下:

:label 
:start 
:end 
:up

看到这种语法是不是很想打人,奇怪又难记。

定义了标签之后,如果要跳转到某个标签名,可以使用 t 命令。

t命令的语法格式为

t [label_name]

其中[label_name] 为要跳转到的标签名。这是一个可选项,如果忽略,那么 sed 会跳转到 sed 命令文件的末尾,也就是结束所有 sed 命令。

sed 循环语句

sed中的循环语句的一般流程如下:

1、 先使用标签语法:<label>定义一个标签label;
2、 然后定义条件为真时要执行的语句,也就循环语句的代码;
3、 最后使用**<condition>t**命令判断循环条件condition的真假,如果为真则跳转到刚刚定义的标签label处,为假则继续往下执行语句;

因此,一个简单的 sed 循环语句的语法格式如下

:label
some_code_here
/<pattern/t label

当然了,我们也可以对条件取反,也就是条件测试不通过时才跳转,这时候就要用到逻辑非运算符 ! 了,具体的语法格式如下

:label
some_code_here
/<pattern/!t label

范例

看了有点复杂啊,我们来写一个范例吧。不过呢,看了范例感觉更复杂...五味杂陈

我们下来看看我们要处理的数据文件,是当前目录下的一个叫 data.txt 的文件,内容如下

I am 
studing sed
I am 
www.ddkk.com
I am 
a no-work-men
I am 
so handsome

就是一些 我是... 的语句。

这时候,我想再说一次,我是傻逼,竟然学这么复杂的 sed 语言。

这个范例,我们需要做的就是把相邻的两行组合在一起,也就是把第一行和第二行组合在一起,第二行和第三行组合在一起...,然后把行中的换行符(\n)替换为 冒号(:) 。接着查找行中是否有 ddkk 字符串,如果有的话在行的开头追加四个 ---- 。如果没有则直接跳转到 Print 标签处直接输出。

这里我们并不是用条件判断来实现一次性追加四个 -----,而是使用循环语句,一次只追加一个 -。

要实现的效果如下

I am :studing sed
----I am :www.ddkk.com
I am :a no-work-men
I am :so handsome

这个效果,用文字描述真的很简单,可是,实现的代码却有点复杂了

[www.ddkk.com]$ sed -n ' 
h;n;H;x;
s/\n/:/ 
:Loop 
/ddkk/s/^/-/ 
/----/!t Loop 
p' data.txt

运行上面的 sed 程序,输出结果如下

I am :studing sed
----I am :www.ddkk.com
I am :a no-work-men
I am :so handsome

看到上面代码的第一眼,我怎么觉得所有字母都认识,但是放到一起就不认识了呢

  • 第一行 h;n;H;x 语句用于把相邻的两行放到一起

  • h 命令拷贝模板块的内容到内存中的缓冲区

  • n 命令读取下一个输入行,用下一个命令处理新的行而不是第一个命令

  • H 命令追加模板块的内容到内存中的缓冲区

  • x 命令表示互换模板块中的文本和缓冲区中的文本

  • 第二行 s/\n/, / 把模板块的文本中的 换行符(\n) 替换成 :。

  • 第三行 :Loop 定义了一个标签 Loop。

  • 第四行是模式查找和替换语句,首先 /ddkk/s 用于查找字符串 ddkk。如果找到则运行后面的语句 ^/-/ 就是在行的开头插入一个 -。

  • 第五个命令是一个 条件测试 语句,如果没找到 ---- 则跳转到标签 Loop,如果找到则继续执行。

  • 第六个命令,p 命令,想必你是知道的,是 输出命令

上面的sed 程序,为了方便阅读,我们把每一个 sed 命令都放在了单独的一样。其实更多时候,我们会把它们放到一行上,多条 sed 命令之间使用 分号(;) 分隔。

就像下面这样

[www.ddkk.com]$ sed -n 'h;n;H;x; s/\n/, /; :Loop;/ddkk/s/^/-/; /----/!t Loop; p' data.txt

运行上面的 sed 程序,输出结果如下

I am : studing sed
- I am : www.ddkk.com
I am : a no-work-men
I am : so handsome

疑问?

对于上面这个循环程序,不知道你是不是有和我当初一样的疑问?

为什么不会陷入死循环?

面对组合后的第一行文本 I am :studing sed,显然是不满足替换的,也就不会在开始添加四个 ----,那么条件测试就一定不会通过,不会通过就会回到 Loop 然后一直死循环下去?

这个问题,我不知道要如何解释,我么把源程序改一下,改成

sed -n ' 
h;n;H;x;
s/\n/:/ 
:Loop
p
/ddkk/s/^/-/ 
/---------/!t Loop 
' data.txt

也就是说,我们只在循环里输出一次

输出结果如下

I am :studing sed
I am :studing sed
I am :www.ddkk.com
-I am :www.ddkk.com
--I am :www.ddkk.com
---I am :www.ddkk.com
----I am :www.ddkk.com
-----I am :www.ddkk.com
------I am :www.ddkk.com
-------I am :www.ddkk.com
--------I am :www.ddkk.com
I am :a no-work-men
I am :a no-work-men
I am :so handsome
I am :so handsom

大家看到什么结果了没有?

sed会统计 t 命令的失败次数,如果超过 2 次失败,则不会继续测试了....