正则表达式入门:示例
在正则表达式简介中,我介绍了正则表达式是什么以及它们为什么有用。现在,我们需要一个真实的例子来作为学习工具。这是我几年前遇到的一个例子。
此示例突出了 Linux 命令行(尤其是正则表达式)的强大功能和灵活性,因为它们能够自动执行常见任务。在我的职业生涯中,我管理过多个列表服务,现在仍在管理。人们向我发送电子邮件地址,让我将其添加到这些列表中。在多个案例中,我都收到了 Microsoft Word 格式的姓名和电子邮件地址列表,要将其添加到其中一个列表中。
麻烦的清单
列表本身并不长,但格式不一致。该列表的缩写版本(名称和域名均有更改)如下所示:
Team 1 Apr 3
Leader Virginia Jones vjones88@example.com
Frank Brown FBrown398@example.com
Cindy Williams cinwill@example.com
Marge smith msmith21@example.com
[Fred Mack] edd@example.com
Team 2 March 14
leader Alice Wonder Wonder1@example.com
John broth bros34@example.com
Ray Clarkson Ray.Clarks@example.com
Kim West kimwest@example.com
[JoAnne Blank] jblank@example.com
Team 3 Apr 1
Leader Steve Jones sjones23876@example.com
Bullwinkle Moose bmoose@example.com
Rocket Squirrel RJSquirrel@example.com
Julie Lisbon julielisbon234@example.com
[Mary Lastware) mary@example.com
原始列表有多余的行、需要删除的括号和圆括号等字符、空格和制表符等空白以及一些空行。将这些电子邮件添加到列表所需的格式是 <first> <last> <email@example.com>
。我们的任务是将此列表转换为邮件列表软件可用的格式。
显然,我需要对数据进行处理,以便将其转换为可以输入到列表中的可接受格式。可以使用文本编辑器或文字处理器(如 LibreOffice Writer)对这个小文件进行必要的更改。但是,人们经常向我发送这样的文件,因此使用文字处理器进行这些更改就变得很麻烦。尽管 Writer 具有良好的搜索和替换功能,但必须单独替换每个字符或字符串,并且无法保存以前的搜索。
Writer 确实具有强大的宏功能,但我对它的两种语言(LibreOffice Basic 和 Python)都不熟悉。但我知道 Bash shell 编程。
我做了系统管理员自然而然会做的事情——我自动执行了这项任务。我做的第一件事是将地址数据复制到文本文件中,以便我可以使用命令行工具对其进行处理。经过几分钟的工作,我开发了上一篇文章中显示的 Bash 命令行程序:
$ cat Experiment_6-1.txt | grep -v Team | grep -v "^\s*$" | sed -e "s/[Ll]eader//" -e "s/\[//g" -e "s/\]//g" -e "s/)//g" | awk '{print $1" "$2" <"$3">"}' > addresses.txt
此代码生成了所需的输出文件addresses.txt
。我使用通常的方法来编写这样的命令行程序,即一次构建一个命令的管道。
让我们将这个管道分解成各个组成部分,看看它是如何工作和组合的。本系列中的所有实验都应以非特权用户身份执行。我还在我为测试创建的虚拟机上执行了此操作:studentvm1
。
示例文件
首先,我们需要创建示例文件。testing
在本地机器上创建一个名为 的目录,然后将下面的文本复制到名为 的新文本文件中 Experiment_6-1.txt
,其中包含上面显示的三个团队条目。
Team 1 Apr 3
Leader Virginia Jones vjones88@example.com
Frank Brown FBrown398@example.com
Cindy Williams cinwill@example.com
Marge smith msmith21@example.com
[Fred Mack] edd@example.com
Team 2 March 14
leader Alice Wonder Wonder1@example.com
John broth bros34@example.com
Ray Clarkson Ray.Clarks@example.com
Kim West kimwest@example.com
[JoAnne Blank] jblank@example.com
Team 3 Apr 1
Leader Steve Jones sjones23876@example.com
Bullwinkle Moose bmoose@example.com
Rocket Squirrel RJSquirrel@example.com
Julie Lisbon julielisbon234@example.com
使用 grep 删除不必要的行
我发现可以做的第一件事是一些简单的事。由于球队名称和日期单独成行,我们可以使用以下命令删除包含单词“Team:”的行
[student@studentvm1 testing]$ cat Experiment_6-1.txt | grep -v Team
我不会重现构建此 Bash 程序的每个阶段的结果,但您应该能够看到数据流的变化,因为它显示在 STDOUT(终端会话)上。我们直到最后才会将其保存在文件中。
在将数据流转换为可用数据的第一步中,我们使用grep
带有简单文字模式的命令Team
。文字是我们可以用作正则表达式的最基本模式类型,因为在被搜索的数据流中只有一个可能的匹配项,那就是字符串Team
。
我们需要丢弃空行,因此我们可以使用另一个grep
语句来消除它们。我发现将第二个命令的正则表达式括grep
在引号中可确保它得到正确解释:
[student@studentvm1 testing]$ cat Experiment_6-1.txt | grep -v Team | grep -v "^\s*$"
Leader Virginia Jones vjones88@example.com
Frank Brown FBrown398@example.com
Cindy Williams cinwill@example.com
Marge smith msmith21@example.com
[Fred Mack] edd@example.com
leader Alice Wonder Wonder1@example.com
John broth bros34@example.com
Ray Clarkson Ray.Clarks@example.com
Kim West kimwest@example.com
[JoAnne Blank] jblank@example.com
Leader Steve Jones sjones23876@example.com
Bullwinkle Moose bmoose@example.com
Rocket Squirrel RJSquirrel@example.com
Julie Lisbon julielisbon234@example.com
[Mary Lastware) mary@example.com
[student@studentvm1 testing]$
该表达式"^\s*$"
说明了锚点,并使用反斜杠 (\) 作为转义字符,将文字“s”(在本例中)的含义更改为元字符,该元字符表示任何空格,例如空格、制表符或其他不可打印的字符。我们在文件中看不到这些字符,但它确实包含其中一些。
星号,又称 splat (*),指定我们要匹配零个或多个空白字符。此添加将匹配多个制表符、多个空格或原本为空的行中的任意组合。
使用 Vim 查看多余的空格
接下来,我将 Vim 编辑器配置为使用可见字符显示空格。通过将以下行添加到您自己的~.vimrc
文件或全局/etc/vimrc
配置文件中来执行此操作:
set listchars=eol:$,nbsp:_,tab:<->,trail:~,extends:>,space:+
然后,启动或重新启动 Vim。
我在互联网上搜索如何做到这一点时发现了很多不好的、不完整的、相互矛盾的信息。内置的 Vim 帮助提供了最好的信息,而我从上面创建的数据行对我来说是有用的。
注意: 在下面的示例中,常规空格显示为
+
;制表符显示为<
、、<>
或<–>
,并填充制表符所覆盖的空格的长度。行尾 (EOL) 字符显示为$
。
对文件进行任何操作之前的结果显示如下:
Team+1<>Apr+3~$
Leader++Virginia+Jones++vjones88@example.com<-->$
Frank+Brown++FBrown398@example.com<---->$
Cindy+Williams++cinwill@example.com<--->$
Marge+smith+++msmith21@example.com~$
+[Fred+Mack]+++edd@example.com<>$
$
Team+2<>March+14$
leader++Alice+Wonder++Wonder1@example.com<----->$
John+broth++bros34@example.com<>$
Ray+Clarkson++Ray.Clarks@example.com<-->$
Kim+West++++kimwest@example.com>$
[JoAnne+Blank]++jblank@example.com<---->$
$
Team+3<>Apr+1~$
Leader++Steve+Jones++sjones23876@example.com<-->$
Bullwinkle+Moose+bmoose@example.com<--->$
Rocket+Squirrel+RJSquirrel@example.com<>$
Julie+Lisbon++julielisbon234@example.com<------>$
[Mary+Lastware)+mary@example.com$
使用 sed 删除不必要的字符
您可以看到,我们的文件有很多空格字符需要删除。我们还需要删除单词“leader”,它出现了两次,并且大写一次。我们先删除“leader”。这次,我们将使用sed
(流编辑器)来执行此任务,用新字符串(在我们的例子中为空字符串)替换它匹配的模式。
添加sed -e "s/[Ll]eader//"
到管道可以执行以下操作:
[student@studentvm1 testing]$ cat Experiment_6-1.txt | grep -v Team | grep -v "^\s*$" | sed -e "s/[Ll]eader//"
在此sed
命令中,-e
表示引号括起来的表达式是一个产生所需结果的脚本。在表达式中,s
表示这是一个替换。替换的基本形式是s/<regex>/<replacement string>/
,/[Ll]eader/
我们的搜索字符串也是如此。
该集合与或[Ll]
匹配,因此与 或匹配。在这种情况下,替换字符串为空,因为它看起来像双正斜杠,但两个斜杠之间没有字符或空格 ( )。L
l
[Ll]eader
leader
Leader
//
让我们摆脱一些[]()
不需要的无关字符:
[student@studentvm1 testing]$ cat Experiment_6-1.txt | grep -v Team | grep -v "^\s*$" | sed -e "s/[Ll]eader//" -e "s/\[//g" -e "s/]//g" -e "s/)//g" -e "s/(//g"
我们在语句中添加了四个新表达式sed
。每个表达式都会删除一个字符。第一个新表达式有点不同,因为左方括号 ( [
) 字符可以标记一个集合的开始。我们需要对括号进行转义,以确保将sed
其正确解释为常规字符而不是特殊字符。
使用 awk 进行整理
我们可以使用sed
从某些行中删除前导空格,但该awk
命令可以做到这一点,如果需要重新排序字段,并<>
在电子邮件地址周围添加字符:
[student@studentvm1 testing]$ cat Experiment_6-1.txt | grep -v Team | grep -v "^\s*$" | sed -e "s/[Ll]eader//" -e "s/\[//g" -e "s/]//g" -e "s/)//g" -e "s/(//g" | awk '{print $1" "$2" <"$3">"}'
该awk
实用程序实际上是一种功能强大的编程语言,可以在其 STDIN 上接受数据流。这一事实使其在命令行程序和脚本中非常有用。
该awk
实用程序适用于数据字段,默认字段分隔符是空格 - 任意数量的空格。到目前为止,我们创建的数据流有三个由空格( 、 和 )分隔<first>
的<last>
字段<email>
:
awk '{print $1" "$2" <"$3">"}'
这个小程序提取三个字段($1
、$2
和$3
)中的每一个,不带前导或尾随空格地提取它们。然后按顺序打印它们,在每个字段之间添加一个空格以及<>
包含电子邮件地址所需的字符。
总结
这里的最后一步是将输出数据流重定向到文件,但这是微不足道的,所以我把这一步留给你来执行。其实你没有必要这么做。
我将 Bash 程序保存为可执行文件,现在每当我收到新列表时,我都可以运行此程序。其中一些列表相当短,就像本例中的列表一样。其他列表则相当长,有时包含多达几百个地址,并且有许多行“内容”不包含要添加到列表中的地址。
注意:本文是我的 Linux 书籍《使用和管理 Linux:从零到系统管理员》第 2 卷第 6 章的稍作修改的版本,该书将于 2019 年底由 Apress 出版。