Skip to content

sed进阶

未必会用到但是最好理解

多行命令

sed处理的时候只对一行进行处理, 有些短语会被分割为多行

三个用来处理多行文本的特殊命令

  • N: 将数据流中的下一行加进来创建一个多行组(multiline group)来处理。
  • D:删除多行组中的一行。
  • P:打印多行组中的一行。

next命令

单行的next命令

小写的n会告诉sed编辑器移动到数据流中的下一文本行,而不用重新回到命令的最开始再执行一遍

sed编辑器在移动到数据流中的下一文本行之前,会在当前行上执行完所有定义好的命令。单行next命令改变了这个流程

**个人理解:**对数据进行查找, 找到之后对下一行进行处理

bash
$ cat data1.txt
This is the header line. 

This is a data line.

This is the last line.
$ sed '/header/{n ; d}' data1.txt 
This is the header line.
This is a data line.
This is the last line.

合并文本行

单行next命令会将数据流中的下一文本行移动到sed编辑器的工作空间(称为模式空间)。多行版本的next命令(用大写N)会将下一文本行添加到模式空间中已有的文本后。

**注: **每两行为一组, 不进行重复的测试

bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/20$ sed 'N; s/3\n/ /' data1.txt 
Line 1
Line 2
Line  Line 4
Line 5
jiao@jiao-virtual-machine:~/桌面/linux-shell/20$ sed 'N; s/4\n/ /' data1.txt 
Line 1
Line 2
Line 3
Line 4
Line 5

文本行仍然用换行符分隔,但sed编辑器现在会将两行文本当成一行来处理

bash
sed '/first/{ N ; s/\n/ / }' data2.txt 
This is the header line. 
This is the first data line. This is the second data line. 
This is the last line.

合并两行

如果要在数据文件中查找一个可能会分散在两行中的文本短语的话,这是个很实用的应用程序。这里有个例子

bash
$ cat data3.txt
On Tuesday, the Linux System 
Administrator's group meeting will be held.
All System Administrators should attend. 
Thank you for your attendance. 
sed 'N ; s/System.Administrator/Desktop User/' data3.txt		# 使用.代表空格或者换行符
On Tuesday, the Linux Desktop User's group meeting will be held.
All Desktop Users should attend.
Thank you for your attendance.

如果是换行符, 会把他替换为空格, 导致两行合并为一行

可以使用多种替换方式

bash
$ sed 'N 
> s/System\nAdministrator/Desktop\nUser/ 
> s/System Administrator/Desktop User/ 
> ' data3.txt

这个脚本总是在执行sed编辑器命令前将下一行文本读入到模式空间。当它到了最后一行文本时,就没有下一行可读了,所以N命令会叫sed编辑器停止。如果要匹配的文本正好在数据流的最后一行上,命令就不会发现要匹配的数据

可以把处理单行的格式放在N前面

bash
$ sed ' 
> s/System Administrator/Desktop User/ 
> N 
> s/System\nAdministrator/Desktop\nUser/ 
> ' data4.txt

多行删除命令

单行删除命令(d)。sed编辑器用它来删除模式空间中的当前行。但和N命令一起使用时,使用单行删除命令就要小心了

会把两行全部删除, 如果只想删除第一行, 应该使用D命令, 只删除到换行符

可以用来结合删除特定位置的空白行

多行打印命令

P命令, 只打印第一行的内容

保持空间

模式空间是一块活跃的缓冲区, sed编辑器会检查他保存的文本

有另一块称作保持空间(hold space)的缓冲区域。在处理模式空间中的某些行时,可以用保持空间来临时保存一些行。

有5条命令可用来操作保持空间

命令描述
h将模式空间复制到保持空间
H将模式空间附加到保持空间
g将保持空间复制到模式空间
G将保持空间附加到模式空间
x交换模式空间和保持空间的内容

可以用来反向输出两行

bash
$ sed -n '/first/ {h ; n ; p ; g ; p }' data2.txt
This is the second data line. 
This is the first data line.

排除命令

可以使用命令来通过地址排除某些行

使用( ! )来排除某些行时候调用的命令

在处理N命令的时候不能识别最后一行, 可以使用排除命令

bash
$ sed '$!N; 
> s/System\nAdministrator/Desktop\nUser/ 
> s/System Administrator/Desktop User/ 
> ' data4.txt

可用来反转一段对话的输出顺序

把每一行的先放入保持空间, 然后在下一行的时候把他放到模式空间后面, 在最后一行的时候进行打印

bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/20$ sed -n '1!G; h ; $p' data1.txt 
Line 5
Line 4
Line 3
Line 2
Line 1

tac命令会倒序显示一个文本文件。你也许已经注意到了,这个命令的名字很巧妙,它执行的正好是与cat命令相反的功能。

改变流

通常,sed编辑器会从脚本的顶部开始,一直执行到脚本的结尾(D命令是个例外,它会强制sed编辑器返回到脚本的顶部,而不读取新的行)

分支

sed编辑器提供了一种方法,可以基于地址、地址模式或地址区间排除一整块命令。

使用命令b

bash
[address]b [label]

address参数决定了哪些行的数据会触发分支命令。label参数定义了要跳转到的位置。如果没有加label参数,跳转命令会跳转到脚本的结尾

bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/20$ sed '2,3b ; s/Line/line/; s/[15]/?/' data1.txt 
line ?
Line 2
Line 3
line 4
line ?

不对2,3行进行处理

要是不想直接跳到脚本的结尾,可以为分支命令定义一个要跳转到的标签。标签以冒号开始,最多可以是7个字符长度

bash
: label
bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/20$ sed '2,3b jump; s/Line/line/;: jump; s/[135]/?/' data1.txt 
line ?
Line 2
Line ?
line 4
line ?

可以向前跳跳转形成循环

bash
$ echo "This, is, a, test, to, remove, commas." | sed -n '{ 
> :start 
> s/,//1p 
> /,/b start 			# 加一个条件防止形成死循环
> }'

测试

t命令, 测试命令会根据替换命令的结果跳转到某个标签,而不是根据地址进行跳转

替换命令成功匹配并替换了一个模式,测试命令就会跳转到指定的标签。如果替换命令未能匹配指定的模式,测试命令就不会跳转

bash
[address]t [label]

跟分支命令一样,在没有指定标签的情况下,如果测试成功,sed会跳转到脚本的结尾

bash
$ echo "This, is, a, test, to, remove, commas. " | sed -n '{ 
> :start 
> s/,//1p 
> t start 		# 只有在上面替换成功的时候进行循环, 去除所有的','
> }'

模式替代

在使用通配符的时候很难知道什么文本会被匹配

&符号

sed编辑器提供了一个解决办法。&符号可以用来代表替换命令中的匹配的模式。不管模式匹配的是什么样的文本,你都可以在替代模式中使用&符号来使用这段文本。

bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/20$ echo "The cat set in its hat" | sed 's/.at/"&"/g'
The "cat" set in its "hat"

替代单独的单词

有时候你只想使用一部分

sed编辑器用圆括号来定义替换模式中的子模式。你可以在替代模式中使用特殊字符来引用每个子模式。替代字符由反斜线和数字组成。数字表明子模式的位置。sed编辑器会给第一个子模式分配字符\1,给第二个子模式分配字符\2,依此类推

bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/20$ echo "This is a test text" | sed 's/a \(test\)/\1/'
This is test text

用来处理替换的对象有一部分为原数据

  • 用来在大串的数字中插入逗号
bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/20$ echo "123456789" | sed '{:start; s/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/; t start}'		# 匹配的字符串是最起码有四个数字, 把后面的三个数字分为一组
123,456,789

在脚本中使用sed

使用包装脚本

实现sed脚本的过程十分繁琐, 尤其是脚本很长的话, 可以把脚本放到包装脚本中

包装脚本充当着sed编辑器脚本和命令行之间的中间人角色

bash
  1 #!/bin/bash                                                                           
  2 
  3 sed -n '{ 1!G; h ; $p }' $1


jiao@jiao-virtual-machine:~/桌面/linux-shell/20$ test2.sh data1.txt 
Line 5
Line 4
Line 3
Line 2
Line 1

重定向sed输出

正常情况下会出出到STDOUT上

创建实用工具

加倍行间距

加倍行间距

bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/20$ sed 'G' data1.txt 
Line 1

Line 2

Line 3

Line 4

Line 5

jiao@jiao-virtual-machine:~/桌面/linux-shell/20$

可以使用!$G不添加最后一行

对可能已经存在空行的文件加倍行距

bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/20$ sed '/^$/d ;$!G' data1.txt 
Line 1

Line 2

Line 3

Line 4

Line 5

给文件的行标号

使用等号进行编号有些难看

bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/20$ sed '=' data1.txt | sed 'N; s/\n/ /'
1 Line 1
2 Line 2
3 Line 3
4 Line 4
5 Line 5
jiao@jiao-virtual-machine:~/桌面/linux-shell/20$ nl data1.txt 
     1	Line 1
     2	Line 2
     3	Line 3
     4	Line 4
     5	Line 5
jiao@jiao-virtual-machine:~/桌面/linux-shell/20$ cat -n data1.txt 
     1	Line 1
     2	Line 2
     3	Line 3
     4	Line 4
     5	Line 5

其他的方式有可能会增加额外的参数

打印末尾行

bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/20$ sed -n '$p' data1.txt 
Line 5

显示后几行

bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/20$ sed '{
:start
$q; N; 3,$D  # 退出循环; 再插新的入一行 ; 如果已经读取的大于第三行, 删除最上面(只留下两行)
b start
}' data1.txt
Line 4
Line 5

删除行

删除连续的空白行

bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/20$ sed '/./,/^$/!d' data1.txt
Line 1

Line 2

Line 3

Line 4
Line 5

在匹配时候会匹配到有空行以及一个和它相连的行, 不会进行删除, 其他的都删除了, 没有空行的时候会一直进行匹配

删除文件开头的行

bash
/./,$!d

删除结尾

bash
sed '{
:start
/^\n*$/{$d; N ; b start} 
}'

会匹配只有一个换行的行, 检查是不是最后一行, 是的话删除最后一行, 不是的话会添加新的一行, 进行循环

删除html标签

bash
s/<.*>//g

会删除一行中离得最远的大于小于号之间的

bash
s/<[^>]*>//g

删除两个中间没有大于号的尖括号之间的所有东西

会导致出现空行

bash
sed 's/<[^>]*>//g ; /^$/d' data11.txt