前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >sed 多行模式、分支及循环

sed 多行模式、分支及循环

作者头像
用户1148526
发布2024-07-13 09:03:47
830
发布2024-07-13 09:03:47
举报
文章被收录于专栏:Hadoop数据仓库

sed 默认每次只处理一行数据,除非使用 H、G 或者 N 等命令创建多行模式,每行之间用换行符分开。本篇将解释适用于多行模式的 sed 命令。在处理多行模式时,要记住 ^ 只匹配该模式的开头,即最开始一行的开头,且 $ 只匹配该模式的结尾,即最后一行的结尾。

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

就像 H 和 G 一样,大写的命令只会追加内容而不是替换内容。命令 N 从输入文件中读取下一行并追加到模式空间,而不是替换模式空间。 小写命令 n 打印当前模式空间的内容,并清空模式空间,从输入文件中读取下一行到模式空间,然后继续执行后面的命令。大写命令 N 不会打印模式空间内容,也不会清除模式空间内容,而是在当前模式空间内容后加上换行符 \n,并且从输入文件中读取下一行数据,追加到模式空间中,然后继续执行后面的命令。 在下面的例子中,以冒号分隔,打印员工名称和职位。示例文本 empnametitle.txt 的内容如下:

代码语言:javascript
复制
John Doe
CEO
Jason Smith
IT Manager
Raj Reddy
Sysadmin
Anand Ram
Developer
Jane Miller
Sales Manager

在这个文件中,每个员工的姓名和职位位于连续的两行内。下面的命令在同一行上打印以冒号分割的员工名称和职位。

代码语言:javascript
复制
#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 文件内容如下:

代码语言:javascript
复制
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

本例演示在打印 employee.txt 文件内容的同时,以文本方式显示每行的行号:

代码语言:javascript
复制
#sed -e '=' employee.txt | sed '{N;s/\n/ /}'
1 101,John Doe,CEO
2 102,Jason Smith,IT Manager
3 103,Raj Reddy,Sysadmin
4 104,Anand Ram,Developer
5 105,Jane Miller,Sales Manager
#

命令 = 在每个原始行的前面添加一行行号:

代码语言:javascript
复制
#sed -e '=' employee.txt
1
101,John 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 替换成空格。

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

大写的 D、P 功能和小写的 d、p 非常相似,但它们在多行模式中有特殊的功能。小写的命令 p 打印模式空间的内容,大写的 P 也打印模式空间内容,直到它遇到换行符 \n。

下面的例子将打印示例文本 empnametitle.txt 中所有管理者的名称:

代码语言:javascript
复制
#sed -n -e 'N' -e '/Manager/P' empnametitle.txt
Jason Smith
Jane Miller
#
  • N 追加换行符 \n 到当前模式空间(员工名称)的最后,然后从输入文件读取下一行数据并追加进来。因此,当前模式空间内容变为“员工名称\n员工职位”。
  • /Manager/P 打印匹配 Manager 的行,并且只打印员工名称。

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

小写命令 d 会删除模式空间内容,然后读取下一条记录到模式空间,并忽略其后的命令,从头开始下一次循环。大写命令 D,既不会读取下一条记录,也不会完全清空模式空间(除非模式空间内只有一行)。它只会:

  • 删除模式空间的部分内容,直到遇到换行符 \n。
  • 忽略后续命令,在当前模式空间中从头开始执行命令。

假设有下面内容的文件 empnametitle-with-commnet.txt,每个员工的职位都用 @ 包含起来作为注释。需要注意的是,有些注释是跨行的,如 @Information Technology officer@ 就跨了两行。

代码语言:javascript
复制
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@

现在的目标是去掉文件里的注释:

代码语言:javascript
复制
#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 
#

注意这里不能加 -n 选项,因为 P 命令打印的只有职位(因为只有职位带注释):

代码语言:javascript
复制
#sed -n -e '/@/{N;/@.*@/{s/@.*@//;P;D}}' empnametitle-with-commnet.txt
CEO 
IT Manager 
Sysadmin 
Developer 
Sales Manager 
#

也可把上述命令写到 sed 脚本中然后执行:

创建内容如下的脚本文件 D-upper.sed

代码语言:javascript
复制
#!/bin/sed -f
/@/{
N
/@.*@/{s/@.*@//;P;D}
}

修改脚本文件的模式为可执行

代码语言:javascript
复制
chmod u+x D-upper.sed

执行脚本

代码语言:javascript
复制
#./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 搜索包含 @ 符号的任意行,如果找到,就执行后面的命令;如果没有找到,则读取下一行。
  • N 从输入文件读取下一行,并追加到模式空间。
  • /@.*@/ 在模式空间中搜索匹配 /@.*@/ 的模式,即以 @ 开头和结尾的任何内容。
  • s/@.*@//;P;D 这个中的替换命令把整个注释替换为空(相当于删除)。P 打印模式空间中的第一行,然后 D 删除模式空间中的第一行。然后从头开始执行命令,即不读取下一条记录,又返回到 /@/ 处执行命令。

完整的执行流程如下表所示。

循环次数

模式空间

操作

1

John Doe 空

打印 John Doe =>

2

CEO @Chief Executive Officer@ CEO @Chief Executive Officer@\nJason Smith CEO \nJason Smith CEO \nJason Smith Jason Smith 空

N => s/@.*@// => P CEO => D => 打印 Jason Smith =>

3

IT Manager @Infromation Technology IT Manager @Infromation Technology\nOfficer@ IT Manager IT Manager 空

N => s/@.*@// => P IT Manager => D =>

4

Raj Reddy 空

打印 Raj Reddy =>

5

Sysadmin @System Administrator@ Sysadmin @System Administrator@\nAnand Ram Sysadmin \nAnand Ram Sysadmin \nAnand Ram 空

N => s/@.*@// => P Sysadmin => D =>

6

Developer @Senior Developer @Senior\nProgrammer@ Developer Developer 空

N => s/@.*@// => P Developer => D =>

7

Jane Miller 空

打印 Jane Miller =>

8

Sales Manager @Sales Sales Manager @Sales\nManager@ Sales Manager Sales Manager 空

N => s/@.*@// => P Sales Manager => D =>

对于这个例子而言,可以有以下更简单的写法:

代码语言:javascript
复制
#sed -e '/@/{N;s/@.*@//}' empnametitle-with-commnet.txt
John Doe
CEO 
Jason Smith
IT Manager 
Raj Reddy
Sysadmin 
Anand Ram
Developer 
Jane Miller
Sales Manager 
#

因为注释固定占一行或两行,所以匹配到 @ 就执行 N,取下一行数据到模式空间,然后整体替换掉注释内容即可。这种写法与上面的 P D 写法输出相同,但执行逻辑不同。

完整的执行流程如下表所示。

循环次数

模式空间

操作

1

John Doe 空

打印 John Doe =>

2

CEO @Chief Executive Officer@ CEO @Chief Executive Officer@\nJason Smith CEO \nJason Smith 空

N => s/@.*@// => 打印 CEO \nJason Smith =>

3

IT Manager @Infromation Technology IT Manager @Infromation Technology\nOfficer@ IT Manager 空

N => s/@.*@// => 打印 IT Manager =>

4

Raj Reddy 空

打印 Raj Reddy =>

5

Sysadmin @System Administrator@ Sysadmin @System Administrator@\nAnand Ram Sysadmin \nAnand Ram 空

N => s/@.*@// => 打印 Sysadmin \nAnand Ram =>

6

Developer @Senior Developer @Senior\nProgrammer@ Developer 空

N => s/@.*@// => 打印 Developer =>

7

Jane Miller 空

打印 Jane Miller =>

8

Sales Manager @Sales Sales Manager @Sales\nManager@ Sales Manager 空

N => s/@.*@// => 打印 Sales Manager =>

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

使用标签和分支命令 b,可以改变 sed 的执行流程:

  • :label 定义一个标签。
  • b lable 执行该标签后面的命令。sed 会跳转到该标签,然后执行后面的命令。
  • 命令 b 后面可以不跟任何标签,这种情况下,它会直接跳到 sed 脚本的结尾。

下面例子将把 empnametitle.txt 文件中的员工名称和职位合并到一行内,字段之间以冒号 : 分隔,并且在管理者的名称前面加上一个星号 *。sed 脚本文件 label.sed 内容如下:

代码语言:javascript
复制
#!/bin/sed -nf
h;n;H;x
s/\n/:/
/Manager/!b end
s/^/*/
:end
p

这个脚本中,鉴于之前的例子,已经知道 h;n;H;x 和 s/\n/:/ 的作用了。下面是关于分支的操作:

  • /Manager/!b end 的作用是如果行内不包含关键字 Manager,则跳转到 end 标签(可以任意设置想要的标签名称)。因此,只有匹配 Manager 的员工名称签名,才会执行 s/^/*/ 即在行首加星号 *。
  • :end 即是标签。

给这个脚本加上可执行权限,然后执行:

代码语言:javascript
复制
#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 替代,这样就不用使用保持空间了。而且这个例子只是为了说明命令 b 如何运行,但它不是必须的,等效的简单写法如下:

代码语言:javascript
复制
#sed -n -e 'N;s/\n/:/g;/Manager/s/^/*/g;p;' empnametitle.txt 
John Doe:CEO
*Jason Smith:IT Manager
Raj Reddy:Sysadmin
Anand Ram:Developer
*Jane Miller:Sales Manager
#

5. 使用命令 t 进行循环

命令 t 的作用是,如果前面的命令执行成功,那么就跳转到 t 指定的标签处,继续往下执行后续命令。否则,仍然继续正常的执行流程。 下面例子将把 empnametitle.txt 文件中的员工名称和职位合并到一行内,字段之间以冒号 : 分隔,并且在管理者的名称前面加上三个星号 *。其实只需把上个例子中的替换命令改为 s/^/***/ 即可达到该目的,这里仅仅是为了解释命令 t 是如何运行的。

创建脚本文件 label-t.sed,内容如下:

代码语言:javascript
复制
#!/bin/sed -nf
h;n;H;x
s/\n/:/
:repeat
/Manager/s/^/*/
/\*\*\*/! t repeat
p

然后给脚本文件加上可执行权限并执行:

代码语言:javascript
复制
#chmod u+x label-t.sed
#./label-t.sed empnametitle.txt 
John Doe:CEO
***Jason Smith:IT Manager
Raj Reddy:Sysadmin
Anand Ram:Developer
***Jane Miller:Sales Manager
#

本例中的下面代码执行循环:

代码语言:javascript
复制
:repeat
/Manager/s/^/*/
/\*\*\*/! t repeat
  • /Manager/s/^/*/ 的作用是如果匹配到 Manager,在行首加上星号 *
  • /\*\*\*/!t repeat 的作用是如果没有匹配到三个连续的星号 *,并且前面一行的替换命令成功执行了,则跳转到名为 repeat 的标签处。
  • :repeat 即是标签。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-07-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 读取下一行数据并附加到模式空间(命令 N)
  • 2. 打印多行模式中的第一行(命令 P)
  • 3. 删除多行模式中的第一行(命令 D)
  • 4. 循环和分支(命令 b 和 :label 标签)
  • 5. 使用命令 t 进行循环
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档