. 句点表示任何单个字符
[] 方括号包括一个字符序列
- 短横线在字符之间使用以创建一个序列(在 [] 内)
^ 脱字符用于对序列(在 [] 内)取反
* 星号搜索某个搜索项的零个、一个或多个实例
? 问号搜索某个搜索项的零个或一个实例
+ 加号搜索某个搜索项的一个或多个实例
$ 美元符号搜索行尾
^ 脱字符搜索行首
\ 特殊字符前的反斜杠使该字符成为普通字符
使用 sed 来编辑文件 sed 是流编辑器 (stream editor) 的简写。文本编辑器的传统、现代定义是
可用于创建和编辑文本文件的交互式应用程序。sed 也是一个文本编辑器,但它是 一个命令行实用程序而不是交互式实用程序,从而使之成为一个极其强大的批处理编辑工具。 sed 通常在 UNIX Shell 脚本中用于过滤较大的文本文件集。在本教程的第一部分中,您使用了一个
讨论 golf 的小型测试文件。为了演示 sed 编辑器的高级功能,您将使用一个很小的代码片段, 开发人员可能希望在批处理过程中更改该代码片段。 请将以下文本复制并粘贴到一个名为 sed.txt 的文件中:
system "echo 'project:$project' >> logfile";
system "echo 'version:$version' >> logfile";
system "echo 'optionalid:$optionalid' >> logfile";
system "echo 'nodes:$nodes' >> logfile";
system "echo 'threads:$threads' >> logfile";
|
前面解释过的用于 grep 的所有特殊字符在 sed 中也有效。然而,若要使用 sed , 您必须了解一些附加语法。sed 中的基本表达式由四个部分组成,各个部分之间用正斜杠 (/) 分隔。 以下是用于基本 sed 命令的常见语法: sed s/REGULAREXPRESSION/REPLACEMENTSTRING/flags INPUT_FILE |
s 指示您希望执行搜索和替换。正斜杠用于绑定 sed 中的正则表达式。例如,
如果您只希望将词条 logfile 替换为 logfile.txt ,则可以运行以下命令: sed s/logfile/logfile.txt/ sed.txt |
输出应该类似如下: system "echo 'project:$project' >> logfile.txt";
system "echo 'version:$version' >> logfile.txt";
system "echo 'optionalid:$optionalid' >> logfile.txt";
system "echo 'nodes:$nodes' >> logfile.txt";
system "echo 'threads:$threads' >> logfile.txt";
|
在此情况下要注意的一个要点在于,sed 不会实际更改 sed.txt 的内容。相反, 它将输出发送到标准输出设备。对于这些示例,您将把输出发送到标准输出设备,以便能够立即看到操作结果。 为便于将来参考,可以捕获输出或将其发送到某个新文件。例如,若要将输出发送到 sed_new.txt,可以运行以下命令: sed s/logfile/logfile.txt/ sed.txt > sed_new.txt |
在学习使用斜杠的同时,还有另一个非常重要的特殊字符需要学习。反斜杠 (\) 称为转义字符, 因为它对正则表达式解释中的下一个字符进行转义。更简单的是,将一个反斜杠放在特殊字符前, 将使该字符成为普通项而不是命令项。这非常重要,因为许多文件(尤其是在编写代码的时候)广泛利用了 与用于执行正则表达式的字符相同的字符。在您的 sed.txt 文件中,您会注意到美元符号的使用。 如果您希望替换 $project 而不替换 project ,则需要在搜索和替换中使用转义字符: sed s/\$project/\$project_name/ sed.txt |
您可以在输出中看到 $project 被更改了,但是 project 没有被更改。 system "echo 'project:$project_name' >> logfile";
system "echo 'version:$version' >> logfile";
system "echo 'optionalid:$optionalid' >> logfile";
system "echo 'nodes:$nodes' >> logfile";
system "echo 'threads:$threads' >> logfile";
|
这引入了 sed 中的另一个重要功能。如果您希望同时更改 project 的两个实例, 该怎么办呢?通过到目前为止已学到的知识,合理的回答是只需使用 project 作为正则表达式,但是此回答并不是非常正确。下面将继续并进行尝试, 以便能够演示和解释该过程: sed s/project/project_name/ sed.txt |
在输出中可以看到,project 的第一个实例被更改为 project_name : system "echo 'project_name:$project' >> logfile";
system "echo 'version:$version' >> logfile";
system "echo 'optionalid:$optionalid' >> logfile";
system "echo 'nodes:$nodes' >> logfile";
system "echo 'threads:$threads' >> logfile";
|
然而,第二个实例未更改,尽管它肯定匹配您的正则表达式。您从第一个 示例中知道,sed 似乎更改其输入中的每个匹配字符串,而不是仅更改 第一个匹配字符串,因为它更改 logfile 的每个实例。 区别在于,logfile 的每个实例在单独的行上,而同一行上却有两个 project 实例。 这为什么非常重要?因为 sed 被实现为一个行编辑器。它一次将一个单独的行放到内存中, 并将其作为单个单元来操作。在运行 sed 时务必记住这点,因为所有命令行选项都是按 这个设计原则来设计的(从而使大多数 sed 实现不会受到与系统内存有关的文件大小限制)。 缺省情况下,每一行都视为 sed 命令的一次新的执行。尽管在第一个示例中似乎不是这样, 但是其中 sed 命令仅替换匹配字符串的第一个实例。 然而,您可以简单地使用一个 g 标志来改变此行为。 执行同样的 sed 命令,但这次在结尾附加一个 g : sed s/project/project_name/g sed.txt |
这次,第一行上的两个 project 实例都被更改为 project_name : system "echo 'project_name:$project_name' >> logfile";
system "echo 'version:$version' >> logfile";
system "echo 'optionalid:$optionalid' >> logfile";
system "echo 'nodes:$nodes' >> logfile";
system "echo 'threads:$threads' >> logfile";
|
您可能记得,g 是 global 的简写。 sed 的另一个强大功能是在搜索和替换操作前运行初步搜索,以确定当前是否在您
希望执行命令的行上。这差不多类似于在 sed 中执行 grep 。在您的例子中, 您可能希望更改 node 变量的日志文件,而不是将它与所有其他输出分组在一起。 为此,您需要将字符串 logfile 更改为 logfile_nodes , 但是仅在属于节点的行上执行更改。以下命令可以确切完成此任务: sed /nodes/s/logfile/logfile_nodes/ sed.txt |
下面是其输出: system "echo 'project:$project' >> logfile";
system "echo 'version:$version' >> logfile";
system "echo 'optionalid:$optionalid' >> logfile";
system "echo 'nodes:$nodes' >> logfile_nodes";
system "echo 'threads:$threads' >> logfile";
|
现在,尝试使用一些您在使用 grep 时学习到的正则表达式知识,不过这次是 在 sed 命令中使用。通过在 sed 中使用以下正则表达式,您可以更改以冒号结尾 的每个字符串: sed s/[a-z]*:/value:/g sed.txt |
输出应该类似如下: system "echo 'value:$project' >> logfile";
system "echo 'value:$version' >> logfile";
system "echo 'value:$optionalid' >> logfile";
system "echo 'value:$nodes' >> logfile";
system "echo 'value:$threads' >> logfile";
|
这相当酷,但不是非常合理。它不是非常合理的原因在于,您的所有变量前都有 单词 value ,没有办法对各个变量进行区分。然而,通过使用 sed 的另一个功能, 您可以使这转变为一个实际的示例。 “和”号 (&) 表示与您的正则表达式匹配的字符串。换句话说,如果 [a-z]*: 在某个特定行上被证明为 project: ,则“和”号将包含该值。这会非常有用。 看一下以下这个示例: sed s/[a-z]*:/new_\&/g sed.txt |
这次,您修改了每个匹配字符串,但是保留了与每个变量关联的标识符: system "echo 'new_project:$project' >> logfile";
system "echo 'new_version:$version' >> logfile";
system "echo 'new_optionalid:$optionalid' >> logfile";
system "echo 'new_nodes:$nodes' >> logfile";
system "echo 'new_threads:$threads' >> logfile";
|
使用 sed ,您还可以一次做多件事情。若要一次执行多个命令序列,您必须在每个表达式 前使用 -e 标志。缺省情况下,sed 将第一个参数解释为一个表达式,但是在运行多个命 令时,您需要作出更明确的指定,因此要使用 -e 标志。例如: sed -e s/[a-z]*:/value:/g -e s/logfile/name/g sed.txt |
可以在此例中看到,sed 在适当位置插入了 value: 和 name : system "echo 'value:$project' >> name";
system "echo 'value:$version' >> name";
system "echo 'value:$optionalid' >> name";
system "echo 'value:$nodes' >> name";
system "echo 'value:$threads' >> name";
|
正如您开始看到的,在大规模批处理过程中,sed 可以是个非常强大的文件编辑工具。在前一示例中, 您是在对单个文件进行操作,就像在使用 grep 时所做的那样。不要忘了,这些实用程序的部分强大 功能在于跨多个文件运行它们,这可以使用通配符或文件列表替换单个文件来实现,您已在本教程中这样使用过了。 在命令行上使用 awk 本教程首先对正则表达式进行了基本的解释,然后介绍了 grep 和 sed 。grep 是一个强大的搜索实用程序, 而 sed 则是一个更加强大的搜索和替换实用程序。awk 则更进一步,它在全功能的命令行编程语言中使用正则表达式。 正如 sed 一样,当在命令行上使用 awk 时,它接受基于行的输入。awk 一次解释一行输入,但是与 sed 不同, 它将该行上的每个部分作为变量来处理,这些变量可用作内联代码的输入和输出。 应该指出的是,AWK(大写)是一个可用于编写脚本(而不只是在命令行上使用)的全功能编程语言, 但本教程集中于 awk ,后者是动态解释 AWK 命令的命令行实用程序。 顺便提一下,任何人阅读到这里都会考虑如何实际运用所学到的知识,我刚才就使用 grep 在某些旧代码中搜索理想的 awk 示例:
大多数系统管理员或程序员每天都会看到这些工具的应用。下面是我的输出中的一些行: Edaemon/m_checkcurrentdisk.pl:$freespace = `awk '(NR==1) {print \$4 / 1024 / 1024}' grep.tmp`;
|-------10--------20--------30--------40--------50--------60--------70--------80--------9|
|-------- XML error: The previous line is longer than the max of 90 characters ---------|
Edaemon/m_getdatetime.pl:$month = `awk '(NR==1) {print \$2}' datetime.txt`;
Odaemon/odaemon.beowulf.dvd.pl:$filesize = `awk '(NR==1) {print \$1}' temp.txt`;
|
这些是非常好的示例,因为它们说明了非常基础的 awk 应用。对于您的第一次尝试,甚至可以使它更简单。 对于您的 awk 测试,请在一个空目录中创建以下文件(每个文件的内容无关紧要,并且它们可以是空的)。 Screenshot_1.jpg
Screenshot_2.jpg
Screenshot_3.jpg
awk.txt
regular.txt
sed.txt
|
缺省情况下,awk 读取输入文件中的每一行,并将内容分离为由空格确定的变量。在非常简单的示例中, 您可以使用 ls 的输出作为 awk 的输入并打印结果。此示例结合使用管道字符 (|) 和 ls 来将输出发送到 awk : ls | awk ' { print $1 } ' |
awk 随后打印每行上的第一项,在此例中为每行上的唯一项:
Screenshot_1.jpg
Screenshot_2.jpg
Screenshot_3.jpg
awk.txt
regular.txt
sed.txt
|
这确实是非常基本的功能。对于下一个示例,请使用 ls -l 来为 awk 生成多列输入:
不同系统的 ls 实现稍有差别,下面是一些示例输出: total 432
-rw-rw-rw- 1 guest guest 169074 Oct 15 14:51 Screenshot_1.jpg
-rw-rw-rw- 1 guest guest 23956 Oct 15 20:56 Screenshot_2.jpg
-rw-rw-rw- 1 guest guest 12066 Oct 15 20:57 Screenshot_3.jpg
-rw-r--r-- 1 tuser tuser 227 Oct 15 20:16 awk.txt
-rw-r--r-- 1 tuser tuser 233 Oct 15 19:35 regular.txt
-rw-r--r-- 1 tuser tuser 227 Oct 15 23:16 sed.txt
|
请注意,文件所有者是每行上的第三个项,文件名是每行上的第九个项(缺省情况下,awk 中的项之间用空格分隔)。 通过打印每行上的第三和第九个变量,您可以使用 awk 来从该列表提取文件所有者和文件名。下面是完成这项工作的命令: ls -l | awk ' { print $3 " " $9 } ' |
您将注意到,awk 中的 print 命令有两个引号,而且引号中有一个空格。这只是为了在输出的文件所有者和文件名之间打印一个空格: guest Screenshot_1.jpg
guest Screenshot_2.jpg
guest Screenshot_3.jpg
tuser awk.txt
tuser regular.txt
tuser sed.txt
|
您可以在 awk 打印语句中的变量之间的引号中放置任何文本。 现在您已经学习了如何使用 awk 的基础知识,但本教程不是关于正则表达式的吗? awk 中大量使用了正则表达式。最常见的示例是在 awk 命令前附加一个正则表达式,用
于指定您想要操作的行。与 sed 一样,awk 中的正则表达式位于两个正斜杠之间。 例如,如果您只希望操作 tuser 所拥有的文件,则可以使用以下命令: ls -l | awk ' /tuser/ { print $3 " " $9 } ' |
该命令产生以下输出: tuser awk.txt
tuser regular.txt
tuser sed.txt
|
在另一个示例中,您可能希望更改每个文本文件的文件扩展名而不更改图像文件。 为此,您将需要使用句点而不是空格来分隔输入变量,然后使用正则表达式来指示您仅希望搜索文本文件。 若要基于句点来分隔变量,可以使用 -F 标志,后跟后跟您希望使用的字符(用引号引起来)。 尝试此示例,通过管道将 awk 输出发送到某个 Shell(此 Shell 将执行 awk 生成的命令): s | awk -F"." ' /txt/ { print "mv " $1 "." $2 " " $1 ".doc" } ' | bash |
后续的 ls -l 将显示新的文件名: -rw-rw-rw- 1 guest guest 169074 Oct 15 14:51 Screenshot_1.jpg
-rw-rw-rw- 1 guest guest 23956 Oct 15 20:56 Screenshot_2.jpg
-rw-rw-rw- 1 guest guest 12066 Oct 15 20:57 Screenshot_3.jpg
-rw-r--r-- 1 tuser tuser 227 Oct 15 20:16 awk.doc
-rw-r--r-- 1 tuser tuser 233 Oct 15 19:35 regular.doc
-rw-r--r-- 1 tuser tuser 227 Oct 15 23:16 sed.doc
|
记住,这些只是 awk 的入门基础知识,但是 AWK 是一种全功能的编程语言, 所具有的功能远远超出了本教程所介绍的内容。请查看一下 awk man 页。如果您希望学习更多的知识, 花钱买一本优秀的图书是明智的。 |