windows ubuntu sed,awk,grep篇:7.sed 多行模式及循环

发布于:2024-05-03 ⋅ 阅读:(29) ⋅ 点赞:(0)
Sed 默认每次只处理一行数据,除非使用 H,G 或者 N 等命令创建多行模式,每行之间用换行
符分开。
本章将解释适用于多行模式的 sed 命令。
提示:在处理多行模式是,请务必牢记 ^ 只匹配该模式的开头,即最开始一行的开头,且 $
匹配该模式的结尾,即最后一行的结尾。

46.读取下一行数据并附加到模式空间(命令 N)

就像大写的命令 H G 一样,只会追加内容而不是替换内容,命令 N 从输入文件中读取下
一行并追加到模式空间,而不是替换模式空间。
前面提到过,小写命令 n 打印当前模式空间的内容,并清空模式空间,从输入文件中读取下
一行到模式空间,然后继续执行后面的命令。
大写命令 N ,不会打印模式空间内容,也不会清除模式空间内容,而是在当前模式空间内容
后加上换行符 \n, 并且从输入文件中读取下一行数据,追加到模式空间中,然后继续执行后
面的命令。
以分号分隔,打印雇员名称和职位 :
$ sed -e '{N;s/\n/:/}' empnametitle.txt
John Doe:CEO
Jason Smith:IT Manager
Raj Reddy:Sysadmin
Anand Ram:Developer
Jane Miller:Sales Manager
这个例子中 :
N 追加换行符 \n 到当前模式空间 ( 雇员名称 ) 的最后,然后从输入文件读取下一行数
据,追加进来。因此,当前模式空间内容变为 雇员名称 \n 雇员职位
s/\n/:/ 把换行符 \n 替换为分号,把分号作为雇员名称和雇员职位的分隔符
流程如下图所示 :
下面的例子将演示在打印 employee.txt 文件内容的同时,以文本方式显示每行的行号:
$ sed -e '=' employee.txt|sed '{N;s/\n/ /}'
1 101,Johnny Doe,CEO
2 102,Jason Smith,IT Manager
3 103,Raj Reddy,Sysadmin
4 104,Anand Ram,Developer
5 105,Jane Miller,Sales Manager
和之前的例子一样,命令 = 先打印行号,然后但印原始的行的内容。
这个例子中,命令 N 在当前模式空间后面加上 \n( 当前模式空间内容为行号 ), 然后读取下一行,
并追究到模式空间中。因此,模式空间内容变为 行号 \n 原始内容 。然后用 s/\n/ / 把换行符
\n 替换成空格。

47.打印多行模式中的第一行(命令 P)

目前为止,我们已经学会了三个大写的命令 (H,N,G) ,每个命令都是追加内容而不是替换内容。
现在我们来看看大写的 D P ,虽然他们的功能和小写的 d p 非常相似,但他们在多行模
式中有特殊的功能。
之前说到,小写的命令 p 打印模式空间的内容。大写的 P 也打印模式空间内容,直到它遇到
换行符 \n 下面的例子将打印所有管理者的名称 :
$ sed -n -e 'N' -e '/Manager/P' empnametitle.txt
Jason Smith
Jane Miller

48. 删除多行模式中的第一行(命令 D)

之前提到,小写命令 d 会删除模式空间内容,然后读取下一条记录到模式空间,并忽略后面
的命令,从头开始下一次循环。
大写命令 D ,既不会读取下一条记录,也不会完全清空模式空间 ( 除非模式空间内只有一行 )
它只会:
z 删除模式空间的部分内容,直到遇到换行符 \n
z 忽略后续命令,在当前模式空间中从头开始执行命令
假设有下面文件,没个雇员的职位都用 @ 包含起来作为注释。需要注意的是,有些注释是跨
行的。如 @Information Technology officer@ 就跨了两行。请先建立下面示例文件 :
$ vi empnametitle-with-commnet.txt
John Doe
CEO @Chief Executive Officer@
Jason Smith
IT Manager @Infromation Technology
Officer@
Raj Reddy
Sysadmin @System Administrator@
Anand Ram
Developer @Senior
Programmer@
Jane Miller
Sales Manager @Sales
Manager@
现在我们的目标是,去掉文件里的注释 :
$ sed -e '/@/{N;/@.*@/{s/@.*@//;P;D}}' empnametitle-with-commnet.txt
John Doe
CEO
Jason Smith
IT Manager
Raj Reddy
Sysadmin
Anand Ram
Developer
Jane Miller Sales Manager
也可把上述命令写到 sed 脚本中然后执行 :
$ vi D-upper.sed
#!/bin/sed -f
/@/{
N
/@.*@/{s/@.*@//;P;D}
}
$ chmod u+x D-upper.sed
$ ./D-upper.sed empnametitle-with-commnet.txt
John Doe
CEO
Jason Smith
IT Manager
Raj Reddy
Sysadmin
Anand Ram
Developer
Jane Miller
Sales Manager
这个例子中 :
  /@/{ 这是外传循环。 Sed 搜索包含 @ 符号的任意行,如果找到,就执行后面的命
令;如果没有找到,则读取下一行。为了便于说明,以第 4 行,即 ”@Information
Technology”( 这条注释跨了两行 ) 为例,它包含一个 @ 符合,所以后面的命令会被执
行。
N 从输入文件读取下一行,并追加到模式空间,以上面提到的那行数据为例,这
N 会读取第 5 行,即 ”Officer@” 并追加到模式空间,因此模式空间内容变
”@Informatioin Technology\nOfficer@”
  /@.*@/ 在模式空间中搜索匹配 /@.*@/ 的模式 , 即以 @ 开头和结尾的任何内容。当
前模式空间的内容匹配这个模式,因此将继续执行后面的命令。
  s/@.*@//;P;D 这个替换命令把整个 @Information Technology\nOfficer@” 替换为空
( 相当于删除 ) P 打印模式空间中的第一行,然后 D 删除模式空间中的第一行,然
后从头开始执行命令 ( 即不读取下一条记录,又返回到 /@/ 处执行命令 )

49.循环和分支(命令 b :label 标签)

使用标签和分支命令 b ,可以改变 sed 的执行流程:
  :label 定义一个标签
b lable 执行该标签后面的命令。 Sed 会跳转到该标签,然后执行后面的命令。
  注意:命令 b 后面可以不跟任何标签,这种情况下,它会直接跳到 sed 脚本的结尾 下面例子将把 empnametitle.txt 文件中的雇员名称和职位合并到一行内,字段之间以分号:
分隔,并且在管理者的名称前面加上一个星号 *
$ cat label.sed
#!/bin/sed -nf
h;n;H;x
s/\n/:/
/Manager/!b end
s/^/*/
:end
p
这个脚本中,鉴于之前的例子,你已经知道 h;n;H;x s/\n/:/ 的作用了。下面是关于分支的
操作:
z /Manager/!b end 如果行内不包含关键字 ”Manager”, 则 跳转到 ’end’ 标签,请注意,
你可以任意设置你想要的标签名称。因此,只有匹配 Manager 的雇员名称签名,
才会执行 s/^/*/( 在行首加上星号 *)
z :end 即是标签
给这个脚本加上可执行权限,然后执行:
$ chmod u+x label.sed
$ ./label.sed empnametitle.txt
John Doe:CEO
*Jason Smith:IT Manager
Raj Reddy:Sysadmin
Anand Ram:Developer
*Jane Miller:Sales Manager
个人觉得脚本里面的 h;n;H;x 可以用一个 N 替代,这样就不用使用保持空间了。
如果不使用标签,还可以: sed 'N;s/\n/:/;/Manager/s/^/\.*/' empnametitle.txt

50.使用命令 t 进行循环

命令 t 的作用是,如果前面的命令执行成功,那么就跳转到 t 指定的标签处,继续往下执行
后续命令。否则,仍然继续正常的执行流程。
下面例子将把 empnametitle.txt 文件中的雇员名称和职位合并到一行内,字段之间以分号:
分隔,并且在管理者的名称前面加上三个星号 *
提示:我们只需把前面例子中的替换命令改为 s/^/***/即可带到该目的,下面这个例子仅仅
是为了解释命令 t 是如何运行的。
$ vi lable-t.sed
#!/bin/sed -nf
h;n;H;x
s/\n/:/
: repeat
/Manager/s/^/*/
/\*\*\*/! t repeat
p
$ chmod u+x lable-t.sed
$ ./lable-t.sed empnametitle.txt
John Doe:CEO
***Jason Smith:IT Manager
Raj Reddy:Sysadmin
Anand Ram:Developer
***Jane Miller:Sales Manager
这个例子中 :
下面的代码执行循环
:repeat
/Manager/s/^/*/
/\*\*\*/! t repeat
  /Manager/s/^/*/ 如果匹配到 Manager, 在行首加上星号 *
  /\*\*\*/!t repeat 如果没有匹配到三个连续的星号 *( /\*\*\*/! 来表示 ) ,并且前面
一行的替换命令成功执行了,则跳转到名为 repeat 的标签处 ( t repeat)
  :repeat 标签

资料来源于《SedandAwk101Hacks》,大家有兴趣可以买一本,也可以关注我,我更新完它。

曾经,我花费大半月将它们跑完,现在啥都忘了,还是要常用。

只为学习交流,不为获利,侵权联系立删。


网站公告

今日签到

点亮在社区的每一天
去签到