网站安全检测可以检测哪些内容风险信息,敬请期待前面一句,学校学院网站建设目标,二次开发包UNIX 世界中有很多文本编辑器可供我们选择。思考一下 — vi、emacs 和 jed 以及很多其它工具都会浮现在脑海中。我们都有自己已逐渐了解并且喜爱的编辑器#xff08;以及我们喜爱的组合键#xff09;。有了可信赖的编辑器#xff0c;我们可以轻松处理任何数量与 UNIX 有关的… UNIX 世界中有很多文本编辑器可供我们选择。思考一下 — vi、emacs 和 jed 以及很多其它工具都会浮现在脑海中。我们都有自己已逐渐了解并且喜爱的编辑器以及我们喜爱的组合键。有了可信赖的编辑器我们可以轻松处理任何数量与 UNIX 有关的管理或编程任务。 虽然交互式编辑器很棒但却有其限制。尽管其交互式特性可以成为强项但也有其不足之处。考虑一下需要对一组文件执行类似更改的情形。您可能会本能地运行自己所喜爱的编辑器然后手工执行一组烦琐、重复和耗时的编辑任务。然而有一种更好的方法。 进入 sed如果可以使编辑文件的过程自动化以便用“批处理”方式编辑文件甚至编写可以对现有文件进行复杂更改的脚本那将太好了。幸运的是对于这种情况有一种更好的方法 — 这种更好的方法称为 “sed”。 sed 是一种几乎包括在所有 UNIX 平台包括 Linux的轻量级流编辑器。sed 有许多很好的特性。首先它相当小巧通常要比您所喜爱的脚本语言小很多倍。其次因为 sed 是一种流编辑器所以它可以对从如管道这样的标准输入接收的数据进行编辑。因此无需将要编辑的数据存储在磁盘上的文件中。因为可以轻易将数据管道输出到 sed所以将 sed 用作强大的 shell 脚本中长而复杂的管道很容易。试一下用您所喜爱的编辑器去那样做。 GNU sed对 Linux 用户来说幸运的是最好的 sed 版本之一恰好是 GNU sed其当前版本是 3.02。每一个 Linux 发行版都有或至少应该有GNU sed。GNU sed 之所以流行不仅因为可以自由分发其源代码还因为它恰巧有许多对 POSIX sed 标准便利、省时的扩展。另外GNU 没有 sed 早期专门版本的很多限制如行长度限制 — GNU 可以轻松处理任意长度的行。 最新的 GNU sed在研究这篇文章之时我注意到几个在线 sed 爱好者提到 GNU sed 3.02a。奇怪的是在ftp.gnu.org有关这些链接请参阅参考资料上找不到 sed 3.02a所以我只得在别处寻找。我在alpha.gnu.org 的 /pub/sed 中找到了它。于是我高兴地将其下载、编译然后安装而几分钟后我发现最新的 sed 版本却是 3.02.80 — 可在alpha.gnu.org 上 3.02a 源代码旁边找到其源代码。安装完 GNU sed 3.02.80 之后我就完全准备好了。 alpha.gnu.orgalpha.gnu.org请参阅参考资料是新的和实验性 GNU 源代码的所在地。然而您还会在那里发现许多优秀、稳定的源代码。出于某种原因不是许多 GNU 开发人员忘记将稳定的源代码移至 ftp.gnu.org就是它们的 “beta” 期间格外长2 年。例如sed 3.02a 已有两年甚至 3.02.80 也有一年但它们仍不能在 2000 年 8 月写本文章时在 ftp.gnu.org 上获得。 正确的 sed在本系列中将使用 GNU sed 3.02.80。在即将出现的本系列后续文章中某些但非常少最高级的示例将不能在 GNU sed 3.02 或 3.02a 中使用。如果您使用的不是 GNU sed那么结果可能会不同。现在为什么不花些时间安装 GNU sed 3.02.80 呢那样不仅可以为本系列的余下部分作好准备而且还可以使用可能是目前最好的 sed。 sed 示例sed 通过对输入数据执行任意数量用户指定的编辑操作“命令”来工作。sed 是基于行的因此按顺序对每一行执行命令。然后sed 将其结果写入标准输出 (stdout)它不修改任何输入文件。 让我们看一些示例。头几个会有些奇怪因为我要用它们演示 sed 如何工作而不是执行任何有用的任务。然而如果您是 sed 新手那么理解它们是十分重要的。下面是第一个示例 $ sed -e ‘d’ /etc/services 如果输入该命令将得不到任何输出。那么发生了什么在该例中用一个编辑命令 ‘d’ 调用 sed。sed 打开 /etc/services 文件将一行读入其模式缓冲区执行编辑命令“删除行”然后打印模式缓冲区缓冲区已为空。然后它对后面的每一行重复这些步骤。这不会产生输出因为 “d” 命令除去了模式缓冲区中的每一行 在该例中还有几件事要注意。首先根本没有修改 /etc/services。这还是因为 sed 只读取在命令行指定的文件将其用作输入 — 它不试图修改该文件。第二件要注意的事是 sed 是面向行的。’d’ 命令不是简单地告诉 sed 一下子删除所有输入数据。相反sed 逐行将 /etc/services 的每一行读入其称为模式缓冲区的内部缓冲区。一旦将一行读入模式缓冲区它就执行 ‘d’ 命令然后打印模式缓冲区的内容在本例中没有内容。我将在后面为您演示如何使用地址范围来控制将命令应用到哪些行 — 但是如果不使用地址命令将应用到所有行。 第三件要注意的事是括起 ‘d’ 命令的单引号的用法。养成使用单引号来括起 sed 命令的习惯是个好注意这样可以禁用 shell 扩展。 另一个 sed 示例下面是使用 sed 从输出流除去 /etc/services 文件第一行的示例 $ sed -e ‘1d’ /etc/services | more 如您所见除了前面有 ‘1′ 之外该命令与第一个 ‘d’ 命令十分类似。如果您猜到 ‘1′ 指的是第一行那您就猜对了。与第一个示例中只使用 ‘d’ 不同的是这一次使用的 ‘d’ 前面有一个可选的数字地址。通过使用地址可以告诉 sed 只对某一或某些特定行进行编辑。 地址范围现在让我们看一下如何指定地址范围。在本例中sed 将删除输出的第 1 到 10 行 $ sed -e ‘1,10d’ /etc/services | more 当用逗号将两个地址分开时sed 将把后面的命令应用到从第一个地址开始、到第二个地址结束的范围。在本例中将 ‘d’ 命令应用到第 1 到 10 行包括这两行。所有其它行都被忽略。 带规则表达式的地址现在演示一个更有用的示例。假设要查看 /etc/services 文件的内容但是对查看其中包括的注释部分不感兴趣。如您所知可以通过以 ‘#’ 字符开头的行在 /etc/services 文件中放置注释。为了避免注释我们希望 sed 删除以 ‘#’ 开始的行。以下是具体做法 $ sed -e ‘/^#/d’ /etc/services | more 试一下该例看看发生了什么。您将注意到sed 成功完成了预期任务。现在让我们分析发生的情况。 要理解 ‘/^#/d’ 命令首先需要对其剖析。首先让我们除去 ‘d’ — 这是我们前面所使用的同一个删除行命令。新增加的是 ‘/^#/’ 部分它是一种新的规则表达式地址。规则表达式地址总是由斜杠括起。它们指定一种 模式紧跟在规则表达式地址之后的命令将仅适用于正好与该特定模式匹配的行。 因此’/^#/’ 是一个规则表达式。但是它做些什么呢很明显现在该复习规则表达式了。 规则表达式复习可以使用规则表达式来表示可能会在文本中发现的模式。您在 shell 命令行中用过 ‘*’ 字符吗这种用法与规则表达式类似但并不相同。下面是可以在规则表达式中使用的特殊字符 字符 描述与行首匹配与行末尾匹配与任一个字符匹配将与前一个字符的零或多个出现匹配[ ] 与 [ ] 之内的所有字符匹配 感受规则表达式的最好方法可能是看几个示例。所有这些示例都将被 sed 作为合法地址接受这些地址出现在命令的左边。下面是几个示例 规则表达式 描述/./ 将与包含至少一个字符的任何行匹配/../ 将与包含至少两个字符的任何行匹配/^#/ 将与以 ‘#’ 开始的任何行匹配/^$/ 将与所有空行匹配/}^/ 将与以 ‘}’无空格结束的任何行匹配/} *^/ 将与以 ‘}’ 后面跟有零或多个空格结束的任何行匹配/[abc]/ 将与包含小写 ‘a’、’b’ 或 ‘c’ 的任何行匹配/^[abc]/ 将与以 ‘a’、’b’ 或 ‘c’开始的任何行匹配 在这些示例中鼓励您尝试几个。花一些时间熟悉规则表达式然后尝试几个自己创建的规则表达式。可以如下使用 regexp $ sed -e ‘/regexp/d’ /path/to/my/test/file | more 这将导致 sed 删除任何匹配的行。然而通过告诉 sed打印 regexp 匹配并删除不匹配的内容而不是与之相反的方法会更有利于熟悉规则表达式。可以用以下命令这样做 $ sed -n -e ‘/regexp/p’ /path/to/my/test/file | more 请注意新的 ‘-n’ 选项该选项告诉 sed 除非明确要求打印模式空间否则不这样做。您还会注意到我们用 ‘p’ 命令替换了 ‘d’ 命令如您所猜想的那样这明确要求 sed 打印模式空间。就这样将只打印匹配部分。 有关地址的更多内容目前为止我们已经看到了行地址、行范围地址和 regexp 地址。但是还有更多的可能。我们可以指定两个用逗号分开的规则表达式sed 将与所有从匹配第一个规则表达式的第一行开始到匹配第二个规则表达式的行结束包括该行的所有行匹配。例如以下命令将打印从包含 “BEGIN” 的行开始并且以包含 “END” 的行结束的文本块 $ sed -n -e ‘/BEGIN/,/END/p’ /my/test/file | more 如果没发现 “BEGIN”那么将不打印数据。如果发现了 “BEGIN”但是在这之后的所有行中都没发现 “END”那么将打印所有后续行。发生这种情况是因为 sed 面向流的特性 — 它不知道是否会出现 “END”。 C 源代码示例如果只要打印 C 源文件中的 main() 函数可输入 $ sed -n -e ‘/main[[:space:]]*(/,/^}/p’ sourcefile.c | more 该命令有两个规则表达式 ‘/main[[:space:]]*(/’ 和 ‘/^}/’以及一个命令 ‘p’。第一个规则表达式将与后面依次跟有任意数量的空格或制表键以及开始圆括号的字符串 “main” 匹配。这应该与一般 ANSI C main() 声明的开始匹配。 在这个特别的规则表达式中出现了 ‘[[:space:]]’ 字符类。这只是一个特殊的关键字它告诉 sed 与 TAB 或空格匹配。如果愿意的话可以不输入 ‘[[:space:]]’而输入 ‘[’然后是空格字母然后是 -V然后再输入制表键字母和 ‘]’ — Control-V 告诉 bash 要插入“真正”的制表键而不是执行命令扩展。使用 ‘[[:space:]]’ 命令类特别是在脚本中会更清楚。 好现在看一下第二个 regexp。’/^}’ 将与任何出现在新行行首的 ‘}’ 字符匹配。如果代码的格式很好那么这将与 main() 函数的结束花括号匹配。如果格式不好则不会正确匹配 — 这是执行模式匹配任务的一件棘手之事。 因为是处于 ‘-n’ 安静方式所以 ‘p’ 命令还是完成其惯有任务即明确告诉 sed 打印该行。试着对 C 源文件运行该命令 — 它应该输出整个 main() { } 块包括开始的 “main()” 和结束的 ‘}’。