之前学习了如何用 sed 编辑器的基本功能来处理数据流中的文本。sed 编辑器的基础命令能满足大多数日常文本编辑需求。本篇文章是继续学习 sed 编辑器提供的更多高级特性所做笔记。这些功能你未必会经常用到,但当需要时,知道这些功能的存在以及如何使用肯定是件好事
1.1 多行命令
在使用 sed 编辑器的基础命令时,你可能注意到了一个局限。所有的 sed 编辑器命令都是针对单行数据执行操作的。在 sed 编辑器读取数据流时,它会基于换行符的位置将数据分成行。sed 编辑器根据定义好的脚本命令一次处理一行数据,然后移到下一行重复这个过程。
举个例子,如果你正在数据中查找短语 Linux System Administrators Group,它很有可能出现在两行中,每行各包含其中一部分短语。如果用普通的 sed 编辑器命令来处理文本,就 不可能发现这种被分开的短语
sed 编辑器包含了三个可用来处理多行文本的特殊命令来解决这个问题:
N:将数据流中的下一行加进来创建一个多行组(multiline group)来处理。
D:删除多行组中的一行。
P:打印多行组中的一行。
1.1.1 next 命令
① 单行的 next 命令
小写的 n 命令会告诉 sed 编辑器移动到数据流中的下一文本行,而不用重新回到命令的开始再执行一遍。记住,通常 sed 编辑器在移动到数据流中的下一文本行之前,会在当前行上执行完所有定义好的命令。单行 next 命令改变了这个流程。
这听起来可能有些复杂,没错,有时确实是。在这个例子中,你有个数据文件,共有 5 行内容,其中的两行是空的。目标是删除首行之后的空白行,而留下后一行之前的空白行。如果写一个删掉空白行的 sed 脚本,你会删掉两个空白行。
![]()
由于要删除的行是空行,没有任何能够标示这种行的文本可供查找。解决办法是用 n 命令。 在这个例子中,脚本要查找含有单词 header 的那一行。找到之后,n 命令会让 sed 编辑器移动到文本的下一行,也就是那个空行
这时,sed 编辑器会继续执行命令列表,该命令列表使用 d 命令来删除空白行。sed 编辑器执行完命令脚本后,会从数据流中读取下一行文本,并从头开始执行命令脚本。因为 sed 编辑器再也找不到包含单词 header 的行了。所以也不会有其他行会被删掉
② 合并文本行
了解了单行版的 next 命令,现在来看看多行版的。单行 next 命令会将数据流中的下一文本行移动到 sed 编辑器的工作空间(称为模式空间)。多行版本的 next 命令(用大写 N)会将下一文本行添加到模式空间中已有的文本后
这样的作用是将数据流中的两个文本行合并到同一个模式空间中。文本行仍然用换行符分隔,但 sed 编辑器现在会将两行文本当成一行来处理
sed 编辑器脚本查找含有单词 first 的那行文本。找到该行后,它会用 N 命令将下一行合并到那行,然后用替换命令 s 将换行符替换成空格。结果是,文本文件中的两行在 sed 编辑器的输出中成了一行。
如果要在数据文件中查找一个可能会分散在两行中的文本短语的话,这是个很实用的应用程序。这里有个例子
替换命令会在文本文件中查找特定的双词短语 System Administrator。如果短语在一行中的话,事情很好处理,替换命令可以直接替换文本。但如果短语分散在两行中的话,替换命令就没法识别匹配的模式了。
这时 N 命令就可以派上用场了
注意,替换命令在 System 和 Administrator 之间**用了通配符模式(.)**来匹配空格和换行符这两种情况。但当它匹配了换行符时,它就从字符串中删掉了换行符,导致两行合并成一行。这可能不是你想要的。
要解决这个问题,可以在 sed 编辑器脚本中用两个替换命令:一个用来匹配短语出现在多行中的情况,一个用来匹配短语出现在单行中的情况
![]()
第一个替换命令专门查找两个单词间的换行符,并将它放在了替换字符串中。
第一个替换命令专门在两个检索词之间寻找换行符,并将其纳入替换字符串。这样就允许你在新文本的同样位置添加换行符了。 但这个脚本中仍有个小问题。这个脚本总是在执行 sed 编辑器命令前将下一行文本读入到模式空间。当它到了后一行文本时,就没有下一行可读了,所以 N 命令会叫 sed 编辑器停止。如果要匹配的文本正好在数据流的后一行上,命令就不会发现要匹配的数据
可以轻松地解决这个问题——将单行命令放到 N 命令 前面,并将多行命令放到 N 命令后面,像这样:
现在,查找单行中短语的替换命令在数据流的后一行也能正常工作,多行替换命令则会负责短语出现在数据流中间的情况
1.1.2 多行删除命令
前面的学习笔记章介绍了单行删除命令(d)。sed 编辑器用它来删除模式空间中的当前行。但和 N 命令一 起使用时,使用单行删除命令就要小心了
删除命令会在不同的行中查找单词 System 和 Administrator,然后在模式空间中将两行都删掉。 这未必是你想要的结果。
** sed 编辑器提供了多行删除命令 D,它只删除模式空间中的第一行。该命令会删除到换行符(含换行符)为止的所有字符**
文本的第二行被 N 命令加到了模式空间,但仍然完好。如果需要删掉目标数据字符串所在行的前一文本行,它能派得上用场。
这里有个例子,它会删除数据流中出现在第一行前的空白行
sed 编辑器脚本会查找空白行,然后用 N 命令来将下一文本行添加到模式空间。如果新的模式 空间内容含有单词 header,则 D 命令会删除模式空间中的第一行。如果不结合使用 N 命令和 D 命令, 就不可能在不删除其他空白行的情况下只删除第一个空白行
1.1.3 多行打印命令
多行打印命令只打印多行模式空间中的第一行。这包括模式空间中直到换行符为止的所有字符。当你用 - n 选项来阻止脚本输出时,它和显示文本的单行 p 命令的用法大同小异
当多行匹配出现时,P 命令只会打印模式空间中的第一行。多行 P 命令的强大之处在和 N 命令及 D 命令组合使用时才能显现出来。
D 命令的独特之处在于强制 sed 编辑器返回到脚本的起始处,对同一模式空间中的内容重新执行这些命令(它不会从数据流中读取新的文本行)。在命令脚本中加入 N 命令,你就能单步扫过整个模式空间,将多行一起匹配。
接下来,使用 P 命令打印出第一行,然后用 D 命令删除第一行并绕回到脚本的起始处。一旦返回,N 命令会读取下一行文本并重新开始这个过程。这个循环会一直继续下去,直到数据流结束
1.2 保持空间
** 模式空间(pattern space)是一块活跃的缓冲区,在 sed 编辑器执行命令时它会保存待检查的 文本。但它并不是 sed 编辑器保存文本的唯一空间**
sed 编辑器有另一块称作保持空间(hold space)的缓冲区域。在处理模式空间中的某些行时, 可以用保持空间来临时保存一些行。有 5 条命令可用来操作保持空间
sed 编辑器的保持空间命令
命 令 描述
h 将模式空间复制到保持空间
H 将模式空间附加到保持空间
g 将保持空间复制到模式空间
G 将保持空间附加到模式空间
x 交换模式空间和保持空间的内容
这些命令用来将文本从模式空间复制到保持空间。这可以清空模式空间来加载其他要处理的 字符串。
通常,在使用 h 或 H 命令将字符串移动到保持空间后,终还要用 g、G 或 x 命令将保存的字符 串移回模式空间(否则,你就不用在一开始考虑保存它们了)。
由于有两个缓冲区域,弄明白哪行文本在哪个缓冲区域有时会比较麻烦。这里有个简短的例 子演示了如何用 h 和 g 命令来将数据在 sed 编辑器缓冲空间之间移动
我们来一步一步看上面这个代码例子:
(1) sed 脚本在地址中用正则表达式来过滤出含有单词 first 的行;
(2) 当含有单词 first 的行出现时,h 命令将该行放到保持空间;
(3) p 命令打印模式空间也就是第一个数据行的内容;
(4) n 命令提取数据流中的下一行(This is the second line),并将它放到模式空间;
(5) p 命令打印模式空间的内容,现在是第二个数据行;
(6) g 命令将保持空间的内容(This is the first line)放回模式空间,替换当前文本;
(7) p 命令打印模式空间的当前内容,现在变回第一个数据行了。
通过使用保持空间来回移动文本行,你可以强制输出中第一个数据行出现在第二个数据行后面。如果丢掉了第一个 p 命令,你可以以相反的顺序输出这两行
这是个有用的开端。你可以用这种方法来创建一个 sed 脚本将整个文件的文本行反转!但要 那么做的话,你需要了解 sed 编辑器的排除特性
1.3 排除命令
在前面学习初始 sed 这一部分时演示了了 sed 编辑器如何将命令应用到数据流中的每一个文本行或是由单个地址或地址 区间特别指定的多行。你也可以配置命令使其不要作用到数据流中的特定地址或地址区间。
感叹号命令(!)用来排除(negate)命令,也就是让原本会起作用的命令不起作用。下面的例子演示了这一特性。
普通 p 命令只打印 data2 文件中包含单词 header 的那行。加了感叹号之后,情况就相反了:除 了包含单词 header 那一行外,文件中其他所有的行都被打印出来了
感叹号在有些应用中用起来很方便。可以用感叹号来解决 sed 编辑器无法处 理数据流中后一行文本的问题
这个例子演示了如何配合使用感叹号与 N 命令以及与美元符特殊地址。美元符表示数据流中的后一行文本,所以当 sed 编辑器到了后一行时**,它没有执行 N 命令**,但它对所有其他行都执 行了这个命令
使用这种方法,你可以反转数据流中文本行的顺序。要实现这个效果(先显示后一行, 后显示第一行),你得利用保持空间做一些特别的铺垫工作。
你得像这样使用模式空间:
(1) 在模式空间中放置一行;
(2) 将模式空间中的行放到保持空间中;
(3) 在模式空间中放入下一行;
(4) 将保持空间附加到模式空间后;
(5) 将模式空间中的所有内容都放到保持空间中;
(6)重复执行第 (3)~(5) 步,直到所有行都反序放到了保持空间中;
(7) 提取并打印行
下图详细描述了整个过程
在使用这种方法时,你不想在处理时打印行。这意味着要使用 sed 的 - n 命令行选项。下一步是决定如何将保持空间文本附加到模式空间文本后面。这可以用 G 命令完成。唯一的问题是你不想将保持空间附加到要处理的第一行文本后面。这可以用感叹号命令轻松解决:
**1!G **
** 下一步就是将新的模式空间(含有已反转的行)放到保持空间。这也非常简单,只要用 h 命令就行。**
** 将模式空间中的整个数据流都反转了之后,你要做的就是打印结果。当到达数据流中的后一行时,你就知道已经得到了模式空间的整个数据流。打印结果要用下面的命令:
$p **
sed 编辑器脚本的执行和预期的一样。脚本输出反转了文本文件中原来的行。这展示了在 sed 脚本中使用保持空间的强大之处。它提供了一种在脚本输出中控制行顺序的简单办法
---------------------------------------
** linux 自带的 tac 命令会倒序显示一个文本文件,这个命令的名字很巧妙,它执行的正好是与 cat 命令相反的功能。**
---------------------------------------
1.4 改变流
通常,sed 编辑器会从脚本的顶部开始,一直执行到脚本的结尾(D 命令是个例外,它会强制 sed 编辑器返回到脚本的顶部,而不读取新的行)。sed 编辑器提供了一个方法来改变命令脚本的执行流程,其结果与结构化编程类似
1.4.1 分支
sed 编辑器提供了 一种方法,可以基于地址、地址模式或地址区间排除一整块命令。这允许你只对数据流中的特定行执行一组命令。 分支(branch)命令 b 的格式如下:
[address] b [label]
address 参数决定了哪些行的数据会触发分支命令。label 参数定义了要跳转到的位置。如果没有加 label 参数,跳转命令会跳转到脚本的结尾
分支命令在数据流中的第 2 行和第 3 行处跳过了两个替换命令。
要是不想直接跳到脚本的结尾,可以为分支命令定义一个要跳转到的标签。标签以冒号开始, 多可以是 7 个字符长度。
:label2
要指定标签,将它加到 b 命令后即可。使用标签允许你跳过地址匹配处的命令,但仍然执行脚本中的其他命令
跳转命令指定如果文本行中出现了 first,程序应该跳到标签为 jump1 的脚本行。如果分支命令的模式没有匹配,sed 编辑器会继续执行脚本中的命令,包括分支标签后的命令(因此,所有的替换命令都会在不匹配分支模式的行上执行)。
** 如果某行匹配了分支模式, sed 编辑器就会跳转到带有分支标签的那行。因此,只有后一个替换命令会执行。**
** 这个例子演示了跳转到 sed 脚本后面的标签上。也可以跳转到脚本中靠前面的标签上,这样就达到了循环的效果**
脚本的每次迭代都会删除文本中的第一个逗号,并打印字符串。这个脚本有个问题:它永远不会结束。这就形成了一个无穷循环,不停地查找逗号,直到使用 Ctrl+C 组合键发送一个信号, 手动停止这个脚本。
** 要防止这个问题,可以为分支命令指定一个地址模式来查找。如果没有模式,跳转就应该结束**
现在分支命令只会在行中有逗号的情况下跳转。在后一个逗号被删除后,分支命令不会再 执行,脚本也就能正常停止了
1.4.2 测试
类似于分支命令,测试(test)命令(t)也可以用来改变 sed 编辑器脚本的执行流程。测试命令会根据替换命令的结果跳转到某个标签,而不是根据地址进行跳转
如果替换命令成功匹配并替换了一个模式,测试命令就会跳转到指定的标签。如果替换命令 未能匹配指定的模式,测试命令就不会跳转
测试命令使用与分支命令相同的格式:
[address]t [label]
跟分支命令一样,在没有指定标签的情况下,如果测试成功,sed 会跳转到脚本的结尾。
测试命令提供了对数据流中的文本执行基本的 if-then 语句的一个低成本办法。举个例子, 如果已经做了一个替换,不需要再做另一个替换,那么测试命令能帮上忙
第一个替换命令会查找模式文本 first。如果匹配了行中的模式,它就会替换文本,而且测试命令会跳过后面的替换命令。如果第一个替换命令未能匹配模式,第二个替换命令就会被执行。
有了测试命令,你就能结束之前用分支命令形成的无限循环
当无需替换时,测试命令不会跳转而是继续执行剩下的脚本
1.5 模式替代
在使用通配符时,很 难知道到底哪些文本会匹配模式;举个例子,假如你想在行中匹配的单词两边上放上引号。如果你只是要匹配模式中的一个单词,那就非常简单
$ echo "The cat sleeps in his hat." | sed 's/cat/"cat"/'
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 前面的一个字母。遗憾的是,用于替代的字符串无法匹配已匹配单词中的通配符字符
** 1.5.1 & 符号**
sed 编辑器提供了一个解决办法。& 符号可以用来代表替换命令中的匹配的模式。不管模式匹配的是什么样的文本,你都可以在替代模式中使用 & 符号来使用这段文本。这样就可以操作模式 所匹配到的任何单词了
当模式匹配了单词 cat,"cat" 就会出现在了替换后的单词里。当它匹配了单词 hat,"hat" 就出现在了替换后的单词中
1.5.2 替代单独的单词
& 符号会提取匹配替换命令中指定模式的整个字符串。有时你只想提取这个字符串的一部分。 当然可以这么做,只是要稍微花点心思而已。
sed 编辑器用圆括号来定义替换模式中的子模式。你可以在替代模式中使用特殊字符来引用每个子模式。替代字符由反斜线和数字组成。数字表明子模式的位置。sed 编辑器会给第一个子 模式分配字符 \ 1,给第二个子模式分配字符 \ 2,依此类推。
--------------------------------------------------------------------------------------------------------------------------------------
警告 当在替换命令中使用圆括号时,必须用转义字符将它们标示为分组字符而不是普通的圆括号。这跟转义其他特殊字符正好相反
来看一个在 sed 编辑器脚本中使用这个特性的例子
这个替换命令用一对圆括号将单词 System 括起来,将其标示为一个子模式。然后它在替代模式中使用 \ 1 来提取第一个匹配的子模式。这没什么特别的,但在处理通配符模式时却特别有用。
如果需要用一个单词来替换一个短语,而这个单词刚好是该短语的子字符串,但那个子字符串碰巧使用了通配符,这时使用子模式会方便很多
在这种情况下,你不能用 & 符号,因为它会替换整个匹配的模式。子模式提供了答案,允许你选择将模式中的某部分作为替代模式。
当需要在两个或多个子模式间插入文本时,这个特性尤其有用。这里有个脚本,它使用子模式在大数字中插入逗号
这个脚本将匹配模式分成了两部分
.*[0-9]
[0-9]{3}
这个模式会查找两个子模式。第一个子模式是以数字结尾的任意长度的字符。第二个子模式是若干组三位数字。如果这个模式在文本中找到了,替代文本会在两个子模式之间加一个逗号,每个子模式都会通过其位置来标示。 这个脚本使用测试命令来遍历这个数字,直到放置好所有的逗号
1.6 在脚本中使用 sed
1.6.1 使用包装脚本
实现 sed 编辑器脚本的过程很烦琐,尤其是脚本很长的话。可以将 sed 编 辑器命令放到 shell 包装脚本(wrapper)中,不用每次使用时都重新键入整个脚本。包装脚本充当着 sed 编辑器脚本和命令行之间的中间人角色
在 shell 脚本中,可以将普通的 shell 变量及参数和 sed 编辑器脚本一起使用。这里有个将命令行参数变量作为 sed 脚本输入的例子
现在你能在任何文件上轻松使用这个 sed 编辑器脚本,再不用每次都在命令行上重新输入了
1.6.2 重定向 sed 的输出
默认情况下,sed 编辑器会将脚本的结果输出到 STDOUT 上。你可以在 shell 脚本中使用各种标 准方法对 sed 编辑器的输出进行重定向。
可以在脚本中用 $() 将 sed 编辑器命令的输出重定向到一个变量中,以备后用。下面的例子使用 sed 脚本来向数值计算结果添加逗号
在使用普通的阶乘计算脚本后,脚本的结果会被作为 sed 编辑器脚本的输入,它会给结果加上逗号。然后 echo 语句使用这个值产生终结果
1.7 创建 sed 实用工具
1.7.1 加倍行间距
首先,让我们看一个向文本文件的行间插入空白行的简单 sed 脚本
这个技巧的关键在于保持空间的默认值。记住,G 命令会简单地将保持空间内容附加到模式空间内容后。当启动 sed 编辑器时,保持空间只有一个空行。将它附加到已有行后面,你就在已有行后面创建了一个空白行
( 也就是说保持空间的默认值是一个空行,使用 sed 的 G 会将保持空间的值附加到模式空间已有行的后面 )
你可能已经注意到了,这个脚本在数据流的后一行后面也加了一个空白行,使得文件的末尾也产生了一个空白行。如果你不想要这个空白行,可以用排除符号(!)和尾行符号($)来确保脚本不会将空白行加到数据流的后一行后面
现在看起来好一些了。只要该行不是后一行,G 命令就会附加保持空间内容。当 sed 编辑器 到了后一行时,它会跳过 G 命令
1.7.2 对可能含有空白行的文件加倍行间距
再进一步探索上面的例子:如果文本文件已经有一些空白行,但你想给所有行加倍行间距要 怎么办呢?如果用前面的脚本,有些区域会有太多的空白行,因为每个已有的空白行也会被加倍
现在,在原来空白行的位置有了三个空白行。这个问题的解决办法是,首先删除数据流中的所有空白行,然后用 G 命令在所有行后插入新的空白行。要删除已有的空白行,需要将 d 命令和一 个匹配空白行的模式一起使用
/^$/d
这个模式使用了行首符号(^)和行尾符号($)。将这个模式加到脚本中会生成想要的结果
1.7.3 给文件中的行编号
如何用等号来显示数据流中行的行号
这可能有点难看,因为行号是在数据流中实际行的上方。比较好的解决办法是将行号和文本 放在同一行。
你已经知道如何用 N 命令合并行,在 sed 脚本中使用这个命令应该不难。这个工具的技巧在于 不能将两个命令放到同一个脚本中。
在获得了等号命令的输出之后,你可以通过管道将输出传给另一个 sed 编辑器脚本,它会使用 N 命令来合并这两行。还需要用替换命令将换行符更换成空格或制表符。最终的解决办法看起来如下
现在看起来好多了。在查看错误消息的行号时,这是一个很好用的小工具。
有些 bash shell 命令也可以添加行号,但它们会另外加入一些东西(有可能是不需要的间隔)
1.7.4 打印末尾行
到目前为止,已经学习了如何用 p 命令来打印数据流中所有的或者是匹配某个特定模式的行。 如果只需处理一个长输出(比如日志文件)中的末尾几行,要怎么办呢?
美元符代表数据流中后一行,所以只显示后一行很容易
那么,如何用美元符来显示数据流末尾的若干行呢?答案是创建滚动窗口。
滚动窗口是检验模式空间中文本行块的常用方法,它使用 N 命令将这些块合并起来。N 命令将下一行文本附加到模式空间中已有文本行后面。一旦你在模式空间有了一个 10 行的文本块,你可以用美元符来检查你是否已经处于数据流的尾部。如果不在,就继续向模式空间增加行,同时删除原来的行(记住,D 命令会删除模式空间的第一行)
通过循环 N 命令和 D 命令,你在向模式空间的文本行块增加新行的同时也删除了旧行。分支命 令非常适合这个循环。要结束循环,只要识别出后一行并用 q 命令退出就可以了
这个脚本会首先检查这行是不是数据流中后一行。如果是,退出(quit)命令会停止循 环。N 命令会将下一行附加到模式空间中当前行之后。如果当前行在第 10 行后面,11,$D 命令会删除模式空间中的第一行( 11,$D 就是从第 11 行开始依次从第 1 行开始删除,即第 10 行之后有几行,就从首行开始删除几行 )。这就会在模式空间中创建出滑动窗口效果。因此,这个 sed 程序脚本只会显示出 data7.txt 文件后 10 行
1.7.5 删除行
另一个有用的 sed 编辑器工具是删除数据流中不需要的空白行。删除数据流中的所有空白行 很容易,但要选择性地删除空白行则需要一点创造力。这个小结的学习笔记将会给出一些简短的 sed 编辑器脚本, 它们可以用来帮助删除数据中不需要的空白行
① 删除连续的空白行
** 数据文件中出现多余的空白行会非常让人讨厌。通常,数据文件中都会有空白行,但有时由 于数据行的缺失,会产生过多的空白行(就像之前加倍行间距例子中所见到的那样)。**
** 删除连续空白行的简单办法是用地址区间来检查数据流。前面的学习笔记介绍了如何在地址中使用 区间,包括如何在地址区间中加入模式。sed 编辑器会对所有匹配指定地址区间的行执行该命令。
删除连续空白行的关键在于创建包含一个非空白行和一个空白行的地址区间。如果 sed 编辑 器遇到了这个区间,它不会删除行。但对于不匹配这个区间的行(两个或更多的空白行),它会 删除这些行。**** 下面是完成这个操作的脚本:**
** /./,/^$/!d **
** 区间是 /./ 到 /^$/。区间的开始地址会匹配任何含有至少一个字符的行。区间的结束地址会 匹配一个空行。在这个区间内的行不会被删除 **
无论文件的数据行之间出现了多少空白行,在输出中只会在行间保留一个空白行
** ② 删除开头的空白行**
数据文件开头有多个空白行时也很烦人。通常,在将数据从文本文件导入到数据库时,空白行会产生一些空项,涉及这些数据的计算都得作废。
删除数据流顶部的空白行不难。下面是完成这个功能的脚本
/./,$!d
这个脚本用地址区间来决定哪些行要删掉。这个区间从含有字符的行开始,一直到数据流结束。在这个区间内的任何行都不会从输出中删除。这意味着含有字符的第一行之前的任何行都会删除
测试文件在数据行之前有两个空白行。这个脚本成功地删除了开头的两个空白行,保留了数据中的空白行
③ 删除结尾的空白行
** 很遗憾,删除结尾的空白行并不像删除开头的空白行那么容易。就跟打印数据流的结尾一样, 删除数据流结尾的空白行也需要花点心思,利用循环来实现**
注意,在正常脚本的花括号里还有花括号。这允许你在整个命令脚本 中将一些命令分组。该命令组会被应用在指定的地址模式上。地址模式能够匹配只含有一个换行符的行。如果找到了这样的行,而且还是后一行,删除命令会删掉它。如果不是后一行,N 命令会将下一行附加到它后面,分支命令会跳到循环起始位置重新开始
这个脚本成功删除了文本文件结尾的空白行
1.7.6 删除 HTML 标签
现如今,从网站下载文本并将其保存或用作应用程序的数据并不罕见。但当你从网站下载文 本时,有时其中也包含了用于数据格式化的 HTML 标签。如果你只是查看数据,这会是个问题。
标准的 HTML Web 页面包含一些不同类型的 HTML 标签,标明了正确显示页面信息所需要的 格式化功能。这里有个 HTML 文件的例子
HTML 标签由小于号和大于号来识别。大多数 HTML 标签都是成对出现的:一个起始标签(比 如 用来加粗),以及另一个结束标签(比如 用来结束加粗)。
但如果不够小心的话,删除 HTML 标签可能会带来问题。乍一看,你可能认为删除 HTML 标签的办法就是查找以小于号(<)开头、大于号(>)结尾且其中有数据的文本字符串:
s/<.*>//g s 替换命令格式:s / 匹配字段 / 替换字段 / (越学越迷……)
注意,标题文本以及加粗和倾斜的文本都不见了。sed 编辑器将这个脚本忠实地理解为小于号和大于号之间的任何文本,且包括其他的小于号和大于号。每次文本出现在 HTML 标签中(比如 first),这个 sed 脚本都会删掉整个文本。
这个问题的解决办法是让 sed 编辑器忽略掉任何嵌入到原始标签中的大于号。要这么做的话, 你可以创建一个字符组来排除大于号。脚本改为:
s/<[^>]*>//g
[^>]
表示不包含>
的所有字符 ( 个人理解为 s 不替换所有非 ">" 符号的字段 )这个脚本现在能够正常工作了,它会显示你要在 Web 页面 HTML 代码里看到的数据
现在好一些了。要想看起来更清晰一些,可以加一条删除命令来删除多余的空白行。
sed 's/<[^>]*>//g ; /^$/d' data11.txt