Shell Script sed编辑器

交互式文本编辑器(vim)可以用键盘命令来交互式地插入、删除或替换数据中的文本

流编辑器(sed和gawk)则会在编辑器处理数据之前基于预先提供的一组命令来编辑数据流

命令行使用

sed编辑器会执行下列操作。在流编辑器将所有命令与一行数据匹配完毕后,它会读取下一行数据并重复这个过程。在流编辑器处理完流中的所有数据行后,它就会终止。 

(1) 一次从输入中读取一行数据。
(2) 根据所提供的编辑器命令匹配数据。
(3) 按照命令修改流中的数据。
(4) 将新的数据输出到STDOUT。

sed options script file
sed命令选项
-e script 在处理输入时,将script中指定的命令添加到已有命令中
-f file 在处理输入时,将file中指定的命令添加到已有的命令中
-n 不产生命令输出,使用print命令完成输出

默认情况下,sed编辑器会将指定的命令应用到STDIN输入流上。这样你可以直接将数据通过管道输入sed编辑器处理。

$ echo "This is a test" | sed 's/test/big test/'
This is a big test    #s命令会用斜线间指定的第二个文本字符串来替换第一个文本字符串模式
$ cat data1.txt
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
$ sed 's/dog/cat/' data1.txt
The quick brown fox jumps over the lazy cat.
The quick brown fox jumps over the lazy cat.
The quick brown fox jumps over the lazy cat.
The quick brown fox jumps over the lazy cat.
$

sed编辑器并不会修改文本文件的数据。它只会将修改后的数据发送到STDOUT。如果你查看原来的文本文件,它仍然保留着原始数据。

在sed命令行上执行多个命令时,只要用-e选项就可以了,命令之间必须用分号隔开,并且在命令末尾和分号之间不能有空格

$ sed -e 's/brown/green/; s/dog/cat/' data1.txt
The quick green fox jumps over the lazy cat.
The quick green fox jumps over the lazy cat.
The quick green fox jumps over the lazy cat.
The quick green fox jumps over the lazy cat.
$

如果不想用分号,也可以用bash shell中的次提示符来分隔命令。只要输入第一个单引号标示出sed程序脚本的起始(sed编辑器命令列表),bash会继续提示你输入更多命令,直到输入了标示结束的单引号。

$ sed -e '
> s/brown/green/
> s/fox/elephant/
> s/dog/cat/' data1.txt
The quick green elephant jumps over the lazy cat.
The quick green elephant jumps over the lazy cat.
The quick green elephant jumps over the lazy cat.
The quick green elephant jumps over the lazy cat.
$

如果有大量要处理的sed命令,那么将它们放进一个单独的文件中通常会更方便一些。可以在sed命令中用-f选项来指定文件。sed编辑器知道每行都是一条单独的命令。

$ cat script1.sed
s/brown/green/
s/fox/elephant/
s/dog/cat/
$
$ sed -f script1.sed data1.txt
The quick green elephant jumps over the lazy cat.
The quick green elephant jumps over the lazy cat.
The quick green elephant jumps over the lazy cat.
The quick green elephant jumps over the lazy cat.
$

Sed操作命令

  • 替换标记

 替换命令在替换多行中的文本时能正常工作,但默认情况下它只替换每行中出现的第一处。要让替换命令能够替换一行中不同地方出现的文本必须使用替换标记。

s/pattern/replacement/flags

数字,表明新文本将替换第几处模式匹配的地方;

扫描二维码关注公众号,回复: 4270609 查看本文章
$ sed 's/test/trial/2' data4.txt

g,表明新文本将会替换所有匹配的文本;

$ sed 's/test/trial/g' data4.txt

p,会打印与替换命令中指定的模式匹配的行;通常会和sed的-n选项一起使用。-n选项将禁止sed编辑器输出。但p替换标记会输出修改过的行。将二者配合使用的效果就是只输出被替换命令修改过的行

$ sed -n 's/test/trial/p' data5.txt
$ sed -n '/number 3/p' data6.txt
This is line number 3.
$ sed -n '/3/{
> p                    #在修改行之前显示该行
> s/line/test/p
> }' data6.txt
This is line number 3.
This is test number 3.
$

=命令会打印行在数据流中的当前行号。

$ sed -n '/number 4/{
> =
> p
> }' data6.txt
4
This is line number 4.
$

[address]w filename,将替换的结果写到文件中。 

$ sed 's/test/trial/w test.txt' data5.txt    #将修改后的行输出到test.txt

$ sed -n '/Browncoat/w Browncoats.txt' data11.txt    #只将包含文本模式的数据行写入目标文件
$ cat Browncoats.txt
Blum, R Browncoat
Bresnahan, C Browncoat
$

  [address]r filename,将一个独立文件中的数据插入到数据流中

$ cat data12.txt
This is an added line.
This is the second added line.
$
$ sed '3r data12.txt' data6.txt
This is line number 1.
This is line number 2.
This is line number 3.
This is an added line.
This is the second added line.
$ sed '/number 2/r data12.txt' data6.txt
This is line number 1.
This is line number 2.
This is an added line.
This is the second added line.
This is line number 3.

$

有时替换文件中的路径名会比较麻烦,例如正斜线(/)。想用C shell替换/etc/passwd文件中的bash shell:由于正斜线通常用作字符串分隔符,因而如果它出现在了模式文本中的话,必须用反斜线来转义

$ sed 's/\/bin\/bash/\/bin\/csh/' /etc/passwd

sed编辑器允许选择其他字符来作为替换命令中的字符串分隔符:感叹号被用作字符串分隔符

$ sed 's!/bin/bash!/bin/csh!' /etc/passwd
  • 按行寻址操作

默认情况下,在sed编辑器中使用的命令会作用于文本数据的所有行。如果只想将命令作用于特定行或某些行,则必须用行寻址。在sed编辑器中有两种形式的行寻址:1. 以数字形式表示行区间;2. 用文本模式来过滤出行

 两种形式都使用相同的格式来指定地址:

[address]command
#将特定地址的多个命令分组
address {
command1
command2
command3
}
  • 数字方式的行寻址

sed编辑器会将文本流中的第一行编号为1

$ sed '2s/dog/cat/' data1.txt    #作用到指定行号
$ sed '2,3s/dog/cat/' data1.txt    #行地址区间
$ sed '2,$s/dog/cat/' data1.txt    #从某行开始的所有行,可以用特殊地址——美元符
  •  使用文本模式过滤器

sed编辑器允许指定文本模式来过滤出命令要作用的行。/pattern/command.必须用正斜线将要指定的pattern封起来。sed编辑器会将该命令作用到包含指定文本模式的行上.你想只修改用户Samantha的默认shell

$ grep Samantha /etc/passwd
Samantha:x:502:502::/home/Samantha:/bin/bash
$
$ sed '/Samantha/s/bash/csh/' /etc/passwd    #sed编辑器在文本模式中采用了一种称为正则表达式
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
[...]
Christine:x:501:501:Christine B:/home/Christine:/bin/bash
Samantha:x:502:502::/home/Samantha:/bin/csh
Timothy:x:503:503::/home/Timothy:/bin/bash
$
  • 命令组合
$ sed '2{
> s/fox/elephant/
> s/dog/cat/
> }' data1.txt
  • 删除行

$ sed 'd' data1.txt    #删除所有文本行
$ sed '3d' data6.txt    #删除第三行
$ sed '/number 1/d' data6.txt    #模式匹配特性也适用于删除命令,删掉包含匹配指定模式的行
  • 插入和附加文本

插入(insert)命令(i)会在指定行前增加一个新行;

$ sed '3i\
> This is an inserted line.' data6.txt
This is line number 1.
This is line number 2.
This is an inserted line.
This is line number 3.

#要插入或附加多行文本,就必须对要插入或附加的新文本中的每一行使用反斜线,直到最后一行。
sed '1i\
> This is one line of new text.\
> This is another line of new text.' data6.txt

附加(append)命令(a)会在指定行后增加一个新行

$ sed '3a\
> This is an appended line.' data6.txt
This is line number 1.
This is line number 2.
This is line number 3.
This is an appended line.
$ sed '$a\
> This is a new line of text.' data6.txt    #美元符代表最后一行
  • 修改行

change命令允许修改数据流中整行文本的内容 

$ sed '3c\
> This is a changed line of text.' data6.txt
This is line number 1.
This is line number 2.
This is a changed line of text.
This is line number 4.
$ sed '/number 3/c\                #用文本模式来寻址
> This is a changed line of text.' data6.txt
$ sed '2,3c\                        # 一行文替换两行文本
> This is a new line of text.' data6.txt
  • 转换字符

转换(transform)命令(y)是唯一可以处理单个字符的sed编辑器命令 :[address]y/inchars/outchars/转换命令会对inchars和outchars值进行一对一的映射。inchars中的第一个字符会被转换为outchars中的第一个字符,第二个字符会被转换成outchars中的第二个字符。这个映射过程会一直持续到处理完指定字符。如果inchars和outchars的长度不同,则sed编辑器会产生一条错误消息。

$ sed 'y/123/789/' data8.txt
This is line number 7.
This is line number 8.
This is line number 9.
This is line number 4.

多行操作

之前接触的sed编辑器命令都是针对单行数据执行操作的,sed编辑器包含了三个可用来处理多行文本的特殊命令。

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

  • next 命令

单行的next命令

小写的n命令会告诉sed编辑器移动到数据流中的下一文本行,而不用重新回到命令的最开始再执行一遍。目标是删除首行之后的空白行,而留下最后一行之前的空白行。脚本要查找含有单词header的那一行。找到之后,n命令会让sed编辑器移动到文
本的下一行,也就是那个空行。sed编辑器会继续执行命令列表,该命令列表使用d命令来删除空白行。

$ 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命令(用大写N)会将下一文本行添加到模式空间中已有的文本后。这样的作用是将数据流中的两个文本行合并到同一个模式空间中。文本行仍然用换行符分隔,但sed编辑器现在会将两行文本当成一行来处理。

sed编辑器脚本查找含有单词first的那行文本。找到该行后,它会用N命令将下一行合并到那行,然后用替换命令s将换行符替换成空格。结果是,文本文件中的两行在sed编辑器的输出中成了一行。

$ cat data2.txt
This is the header line.
This is the first data line.
This is the second data line.
This is the last line.
$
$ 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.
$

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

$ 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 System
Administrator's group meeting will be held.
All Desktop Users 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.
$

若不想将第一第二行合并可以采用下面方法

$ sed 'N
> s/System\nAdministrator/Desktop\nUser/
> 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.
$

由于System Administrator文本出现在了数据流中的最后一行,N命令会错过它,因为没有其他行可读入到模式空间跟这行合并。你可以轻松地解决这个问题——将单行命令放到N命令前面,并将多行命令放到N命令后面,像这样:

$ sed '
> s/System Administrator/Desktop User/
> N
> s/System\nAdministrator/Desktop\nUser/
> ' data4.txt
On Tuesday, the Linux Desktop
User's group meeting will be held.
All Desktop Users should attend.
$
  • 多行删除命令

将单行删除命令(d)和N命令一起使用时,删除命令会在不同的行中查找单词System和Administrator,然后在模式空间中将两行都删掉。

$ sed 'N ; /System\nAdministrator/d' data4.txt
All System Administrators should attend.
$

sed编辑器提供了多行删除命令D,它只删除模式空间中的第一行。该命令会删除到换行符(含换行符)为止的所有字符。

$ sed 'N ; /System\nAdministrator/D' data4.txt
Administrator's group meeting will be held.
All System Administrators should attend.
$

删除数据流中出现在第一行前的空白行。sed编辑器脚本会查找空白行,然后用N命令来将下一文本行添加到模式空间。如果新的模式空间内容含有单词header,则D命令会删除模式空间中的第一行。如果不结合使用N命令和D命令,就不可能在不删除其他空白行的情况下只删除第一个空白行。

$ cat data5.txt

This is the header line.
This is a data line.

This is the last line.
$
$ sed '/^$/{N ; /header/D}' data5.txt
This is the header line.
This is a data line.

This is the last line.
$
  • 多行打印命令

多行打印命令(P)沿用了同样的方法。它只打印多行模式空间中的第一行。这包括模式空间中直到换行符为止的所有字符。用-n选项来阻止脚本输出时,它和显示文本的单行p命令的用法大同小异。

$ sed -n 'N ; /System\nAdministrator/P' data3.txt
On Tuesday, the Linux System
$

保持空间

模式空间(pattern space)是一块活跃的缓冲区,在sed编辑器执行命令时它会保存待检查的文本。但它并不是sed编辑器保存文本的唯一空间。sed编辑器有另一块称作保持空间(hold space)的缓冲区域。在处理模式空间中的某些行时,可以用保持空间来临时保存一些行。有5条命令可用来操作保持空间,

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

通常,在使用h或H命令将字符串移动到保持空间后,最终还要用g、G或x命令将保存的字符串移回模式空间(否则,你就不用在一开始考虑保存它们了)。

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

排除命令

感叹号命令(!)用来排除(negate)命令,也就是让原本会起作用的命令不起作用。普通p命令只打印data2文件中包含单词header的那行。加了感叹号之后:除了包含单词header那一行外,文件中其他所有的行都被打印出来了。

$ sed -n '/header/!p' data2.txt
This is the first data line.
This is the second data line.
This is the last line.
$

使用这种方法,你可以反转数据流中文本行的顺序,

将保持空间文本附加到模式空间文本后面。这可以用G命令完成。唯一的问题是你不想将保持空间附加到要处理的第一行文本后面。这可以用感叹号命令轻松解决: 1!G

$ cat data2.txt
This is the header line.
This is the first data line.
This is the second data line.
This is the last line.
$
$ sed -n '{1!G ; h ; $p }' data2.txt        #当{}前没命令,本身就是一行行操作
This is the last line.
This is the second data line.
This is the first data line.
This is the header line.
$

改变流

sed编辑器会从脚本的顶部开始,一直执行到脚本的结尾。sed编辑器提供了一个方法来改变命令脚本的执行流程,其结果与结构化编程类似。

  • 分支

已经知道如何用感叹号命令来排除作用在某行上的命令。sed编辑器提供了一种方法,可以基于地址、地址模式或地址区间排除一整块命令。这允许你只对数据流中的特定行执行一组命令。分支(branch)命令b的格式如下:

[address]b [label]

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

$ cat data2.txt
This is the header line.
This is the first data line.
This is the second data line.
This is the last line.
$
$ sed '{2,3b ; s/This is/Is this/ ; s/line./test?/}' data2.txt
Is this the header test?            #分支命令在数据流中的第2行和第3行处跳过了两个替换命令
This is the first data line.
This is the second data line.
Is this the last test?
$

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

$ sed '{/first/b jump1 ; s/This is the/No jump on/
> :jump1                            #定义jump1
> s/This is the/Jump here on/}' data2.txt
No jump on header line
Jump here on first data line        #检测到first,运行jump1的命令行
No jump on second data line
No jump on last line
$

分支不仅可以转到sed脚本后面的标签上。也可以跳转到脚本中靠前面的标签上,这样就达到了循环的效果。 

$ echo "This, is, a, test, to, remove, commas." | sed -n '{
> :start
> s/,//1p                    # 脚本的每次迭代都会删除文本中的第一个逗号,并打印字符串
> b start
> }'
This is, a, test, to, remove, commas.
This is a, test, to, remove, commas.
This is a test, to, remove, commas.
This is a test to, remove, commas.
This is a test to remove, commas.
This is a test to remove commas.
^C
$

这个脚本有个问题:它永远不会结束。这就形成了一个无穷循环,不停地查找逗号,直到使用Ctrl+C组合键发送一个信号,手动停止这个脚本。要防止这个问题,可以为分支命令指定一个地址模式来查找。如果没有模式,跳转就应该结束。 

$ echo "This, is, a, test, to, remove, commas." | sed -n '{
> :start
> s/,//1p          
> /,/b start            #最后一个逗号被删除后,分支命令不会再执行
> }'
This is, a, test, to, remove, commas.
This is a, test, to, remove, commas.
This is a test, to, remove, commas.
This is a test to, remove, commas.
This is a test to remove, commas.
This is a test to remove commas.
$
  • 测试

类似于分支命令,测试(test)命令(t)也可以用来改变sed编辑器脚本的执行流程。测试命令会根据替换命令的结果跳转到某个标签,而不是根据地址进行跳转。如果替换命令成功匹配并替换了一个模式,测试命令就会跳转到指定的标签。如果替换命令未能匹配指定的模式,测试命令就不会跳转。

[address]t [label]

测试命令提供了对数据流中的文本执行基本的if-then语句的一个低成本办法。举个例子,如果已经做了一个替换,不需要再做另一个替换。第一个替换命令会查找模式文本first。如果匹配了行中的模式,它就会替换文本,而且测试命令会跳过后面的替换命令。如果第一个替换命令未能匹配模式,第二个替换命令就会被执行。

$ sed '{
> s/first/matched/
> t
> s/This is the/No match on/
> }' data2.txt
No match on header line
This is the matched data line
No match on second data line
No match on last line
$

有了测试命令,你就能结束上一节中用分支命令形成的无限循环。当无需替换时,测试命令不会跳转而是继续执行剩下的脚本。

$ echo "This, is, a, test, to, remove, commas. " | sed -n '{
> :start
> s/,//1p
> t start
> }'
This is, a, test, to, remove, commas.
This is a, test, to, remove, commas.
This is a test, to, remove, commas.
This is a test to, remove, commas.
This is a test to remove, commas.
This is a test to remove commas.
$

模式替代

举个例子,假如你想在行中匹配的单词两边上放上引号。

$ echo "The cat sleeps in his hat." | sed 's/cat/"cat"/'
The "cat" sleeps in his hat.

但如果你在模式中用通配符(.)来匹配多个单词呢?我们想要的输出结果是The "cat" sleeps in his "hat".

$ echo "The cat sleeps in his hat." | sed 's/.at/".at"/g'
The ".at" sleeps in his ".at".
$

模式字符串用点号通配符来匹配at前面的一个字母。遗憾的是,用于替代的字符串无法匹配已匹配单词中的通配符字符。

  • &符号

 &符号可以用来代表替换命令中的匹配的模式。不管模式匹配的是什么样的文本,你都可以在替代模式中使用&符号来使用这段文本。

$ echo "The cat sleeps in his hat." | sed 's/.at/"&"/g'
The "cat" sleeps in his "hat".
$
  • 替代单独的单词

&符号会提取匹配替换命令中指定模式的整个字符串。有时你只想提取这个字符串的一部分。sed编辑器用圆括号来定义替换模式中的子模式。你可以在替代模式中使用特殊字符来引用每个子模式。替代字符由反斜线和数字组成。sed编辑器会给第一个子模式分配字符\1,给第二个子模式分配字符\2

当在替换命令中使用圆括号时,必须用转义字符将它们标示为分组字符而不是普通的圆括号。这跟转义其他特殊字符正好相反。

这个替换命令用一对圆括号将单词System括起来,将其标示为一个子模式。然后它在替代模式中使用\1来提取第一个匹配的子模式。 

$ echo "The System Administrator manual" | sed '
> s/\(System\) Administrator/\1 User/'    #System User替换System Administrator
The System User manual
$

 如果需要用一个单词来替换一个短语,而这个单词刚好是该短语的子字符串,但那个子字符串碰巧使用了通配符,这时使用子模式会方便很多。在这种情况下,你不能用&符号,因为它会替换整个匹配的模式。子模式提供了答案,允许你选择将模式中的某部分作为替代模式。

$ echo "That furry cat is pretty" | sed 's/furry \(.at\)/\1/'    #cat替换furry cat
That cat is pretty
$
$ echo "That furry hat is pretty" | sed 's/furry \(.at\)/\1/'    #hat替换furry hat
That hat is pretty
$

当需要在两个或多个子模式间插入文本时,这个特性尤其有用。这里有个脚本,它使用子模式在大数字中插入逗号。 这个脚本将匹配模式分成了两部分(.*[0-9])和([0-9]{3})。这个模式会查找两个子模式。第一个子模式是以数字结尾的任意长度的字符。第二个子模式是若干组三位数字。如果这个模式在文本中找到了,替代文本会在两个子模式之间加一个逗号,每个子模式都会通过其位置来标示。这个脚本使用测试命令来遍历这个数字,直到放置好所有的逗号。

$ echo "1234567" | sed '{
> :start
> s/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/        #两个子模式之间加一个逗号
> t start
> }'
1,234,567
$

重定向sed 的输出

默认情况下,sed编辑器会将脚本的结果输出到STDOUT上。你可以在shell脚本中使用各种标准方法对sed编辑器的输出进行重定向。可以在脚本中用$()将sed编辑器命令的输出重定向到一个变量中,以备后用。

$ cat fact.sh
#!/bin/bash
factorial=1
counter=1
number=$1
while [ $counter -le $number ]
do
    factorial=$[ $factorial * $counter ]
    counter=$[ $counter + 1 ]
done
result=$(echo $factorial | sed '{
:start
s/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/
t start
}')

echo "The result is $result"
$

sed 实用工具

  • 加倍行间距

这个技巧的关键在于保持空间的默认值。记住,G命令会简单地将保持空间内容附加到模式空间内容后。当启动sed编辑器时,保持空间只有一个空行。将它附加到已有行后面,你就在已有行后面创建了一个空白行。 

$ sed 'G' data2.txt
This is the header line.

This is the first data line.

This is the second data line.

This is the last line.

$

这个脚本在数据流的最后一行后面也加了一个空白行,使得文件的末尾也产生了一个空白行。如果你不想要这个空白行,可以用排除符号(!)和尾行符号($)来确保脚本不会将空白行加到数据流的最后一行后面。只要该行不是最后一行,G命令就会附加保持空间内容。当sed编辑器到了最后一行时,它会跳过G命令。

$ sed '$!G' data2.txt
This is the header line.

This is the first data line.

This is the second data line.

This is the last line.
$

如果文本文件已经有一些空白行,但你想给所有行加倍行间距要怎么办呢?首先删除数据流中的所有空白行,然后用G命令在所有行后插入新的空白行

$ cat data6.txt
This is line one.
This is line two.

This is line three.
This is line four.
$
$ sed '/^$/d ; $!G' data6.txt
This is line one.

This is line two.

This is line three.

This is line four.
$
  • 给文件中的行编号

这可能有点难看,因为行号是在数据流中实际行的上方。比较好的解决办法是将行号和文本放在同一行。 

$ sed '=' data2.txt
1
This is the header line.
2
This is the first data line.
3
This is the second data line.
4
This is the last line.
$

你已经知道如何用N命令合并行,在sed脚本中使用这个命令应该不难。在获得了等号命令的输出之后,你可以通过管道将输出传给另一个sed编辑器脚本,它会使用N命令来合并这两行。还需要用替换命令将换行符更换成空格或制表符。最终的解决办法看起来如下

$ sed '=' data2.txt | sed 'N; s/\n/ /'
1 This is the header line.
2 This is the first data line.
3 This is the second data line.
4 This is the last line.
$

有些bash shell命令也可以添加行号,但它们会另外加入一些东西

$ nl data2.txt
1 This is the header line.
2 This is the first data line.
3 This is the second data line.
4 This is the last line.
$
$ cat -n data2.txt
1 This is the header line.
2 This is the first data line.
3 This is the second data line.
4 This is the last line.
$
  • 打印末尾行

已经知道如何用p命令来打印数据流中所有的或者是匹配某个特定模式的行。如果只需处理一个长输出(比如日志文件)中的末尾几行。美元符代表数据流中最后一行,所以只显示最后一行很容易。
 

$ sed -n '$p' data2.txt
This is the last line.
$

那么,如何用美元符来显示数据流末尾的若干行呢?答案是创建滚动窗口

滚动窗口是检验模式空间中文本行块的常用方法,它使用N命令将这些块合并起来。N命令将下一行文本附加到模式空间中已有文本行后面。一旦你在模式空间有了一个10行的文本块,你可以用美元符来检查你是否已经处于数据流的尾部。如果不在,就继续向模式空间增加行,同时删除原来的行(记住,D命令会删除模式空间的第一行)。 

通过循环N命令和D命令,你在向模式空间的文本行块增加新行的同时也删除了旧行。分支命令非常适合这个循环。要结束循环,只要识别出最后一行并用q命令退出就可以了。

$ sed '{
> :start
> $q ; N ; 11,$D    #退出(quit)命令;N命令会将下一行附加到模式空间中当前行之后;当前行在第10行后面,11,$D命令会删除模式空间中的第一行
> b start
> }' data7.txt
This is line 6.
This is line 7.
This is line 8.
This is line 9.
This is line 10.
This is line 11.
This is line 12.
This is line 13.
This is line 14.
This is line 15.
$
  • 删除行

删除连续的空白行

/./,/^$/!d

删除连续空白行的关键在于创建包含一个非空白行和一个空白行的地址区间。如果sed编辑器遇到了这个区间,它不会删除行。但对于不匹配这个区间的行(两个或更多的空白行),它会删除这些行。

区间是/./到/^$/。区间的开始地址会匹配任何含有至少一个字符的行。区间的结束地址会匹配一个空行。在这个区间内的行不会被删除。

删除开头的空白行

/./,$!d

这个区间从含有字符的行开始,一直到数据流结束。在这个区间内的任何行都不会从输出中删除。这意味着含有字符的第一行之前的任何行都会删除。

删除结尾的空白行

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

地址模式能够匹配只含有一个换行符的行。如果找到了这样的行,而且还是最后一行,删除命令会删掉它。如果不是最后一行,N命令会将下一行附加到它后面,分支命令会跳到循环起始位置重新开始。

  • 删除HTML 标签

标准的HTML Web页面包含一些不同类型的HTML标签,标明了正确显示页面信息所需要的格式化功能。HTML标签由小于号和大于号来识别。大多数HTML标签都是成对出现的:一个起始标签(比如<b>用来加粗),以及另一个结束标签(比如</b>用来结束加粗)。

$ cat data11.txt
<html>
<head>
<title>This is the page title</title>
</head>
<body>
<p>
This is the <b>first</b> line in the Web page.
This should provide some <i>useful</i>
information to use in our sed script.
</body>
</html>
$

乍一看,你可能认为删除HTML标签的办法就是查找以小于号(<)开头、大于号(>)结尾且其中有数据的文本字符串。
注意,标题文本以及加粗和倾斜的文本都不见了。sed编辑器将这个脚本忠实地理解为小于号和大于号之间的任何文本,且包括其他的小于号和大于号。每次文本出现在HTML标签中(比如<b>first</b>),这个sed脚本都会删掉整个文本。

$ sed 's/<.*>//g' data11.txt
This is the line in the Web page.
This should provide some
information to use in our sed script.
$

这个问题的解决办法是让sed编辑器忽略掉任何嵌入到原始标签中的大于号。要这么做的话,你可以创建一个字符组来排除大于号。

$ sed 's/<[^>]*>//g' data11.txt
This is the page title
This is the first line in the Web page.
This should provide some useful
information to use in our sed script.
$

现在好一些了。要想看起来更清晰一些,可以加一条删除命令来删除多余的空白行。

$ sed 's/<[^>]*>//g ; /^$/d' data11.txt
This is the page title
This is the first line in the Web page.
This should provide some useful
information to use in our sed script.
$

猜你喜欢

转载自blog.csdn.net/linshuo1994/article/details/84228262
今日推荐