Windows PowerShell编写正则表达式

Don Jones

192.168.4.5。 \\Server57\Share。johnd@contoso.com。您马上会认出这三个条目是 IP 地址、通用命名约定 (UNC) 路径以及电子邮件地址。您的大脑识别出了它们的格式。四组数字、反斜线符号、@ 符号以及其他提示都表明了

这些字符串代表的数据类型。不必深思,您可以快速识别出 192.168 本身是一个无效的 IP 地址、7\\Server2\\Share 是无效的 UNC,joe@contoso 也是无效的电子邮件地址。

遗憾的是,计算机必须经过一番努力才能“了解”诸如以上的复杂格式。正则表达式即应运而生。正则表达式是使用特殊正则表达式语言编写而成的字符串,可帮助计算机识别格式特殊的字符串,如 IP 地址、UNC 或电子邮件地址。使用正确编写的正则表达式,Windows PowerShellTM 脚本可接受有效数据或者拒绝与指定格式不符的无效数据。

进行简单匹配

Windows PowerShell –match 运算符将字符串与正则表达式或 Regex 进行比较,然后根据该字符串是否与 Regex 匹配返回 True 或者 False。简单的 regex 甚至不需要包含任何特殊语法,有文字字符即可。例如:

"Microsoft" –match "soft"
"Software" –match "soft"
"Computers" –match "soft"

在 Windows PowerShell 中运行时,前两个表达式返回 True,第三个返回 False。在每个表达式中,字符串后均跟 –match 运算符,然后是 regex。默认情况下,regex 将在字符串中浮动查找匹配项。在 Software 和 Microsoft 中均可找到“soft”字符,但位置不同。另请注意:默认情况下,regex 不区分大小写,所以在“Software”中可找到“soft”,尽管 S 为大写字母。

但如果需要,可使用另一个不同的运算符 –cmatch 进行区分大小写的 regex 比较,如下所示:

"Software" –cmatch "soft"

由于在区分大小写比较中,字符串“soft”与“Software”不匹配,所以该表达式返回 False。请注意:尽管 –match 是默认行为,但也可选择使用 –imatch 运算符显式表示不区分大小写。

通配符和重复字符

regex 可包含若干通配符字符。例如,句点可与一个任意字符实例匹配。问号可与零个或一个任意字符实例匹配。示例如下:

"Don" –match "D.n" (True)
"Dn" –match "D.n" (False)
"Don" –match "D?n" (True)
"Dn" –match "D?n" (True)

在第一个表达式中,句点正好代表一个字符,所以匹配结果为 True。在第二个表达式中,句点未找到需要包含在其中的字符,所以匹配结果为 False。第三个和第四个表达式中的问号可匹配一个未知字符或者不与任何字符匹配。最后,在第四个示例中,由于“D”和“n”均存在并且二者之间没有字符,所以该匹配结果为 True。因此,问号可视为代表可选字符,所以即使在该位置没有出现任何字符,匹配结果仍为 True。

regex 还可将 * 和 + 符号视为重复字符。这些符号需要匹配某个字符或某些字符。* 可与零个或更多指定字符匹配,+ 可与一个或多个指定字符匹配。示例如下:

follow
"DoDon" –match "Do*n" (True)
"Dn" -match "Do*n" (True)
"DoDon" -match "Do+n" (True)
"Dn" -match "Do+n" (False)

请注意:* 和 + 均可与“Do”匹配,而不只是与“o”匹配。这是因为这些重复字符被设计为可匹配一系列字符,而并不只是一个字符。

如果需要匹配句点、*、? 或 + 符号本身,应如何处理呢?可直接在它们前面加上一个反斜杠,作为 regex 转义符:

"D.n" -match "D\.n" (True)

注意:该转义符与 Windows PowerShell 转义符(反单引号)不同,但是也遵循行业标准 regex 语法。

字符类

字符类是通配符的更广泛形式,它代表整组字符。Windows PowerShell 可识别很多字符类。例如:

  • \w 可匹配任何文字字符,即字母和数字。
  • \s 可匹配任何空白字符,如制表符、空格等。
  • \d 可匹配任何数字字符。

还有否定字符类:\W 可匹配非文字字符,\S 可匹配非空白字符,\D 可匹配非数字字符。这些类可后跟 * 或 + 来表示接受多个匹配。示例如下:

"Shell" -match "\w" (True)
"Shell" -match "\w*" (True)

本月 cmdlet

使用 Write-Debug cmdlet,可以轻松地将对象(如文本字符串)写入 Debug 管道。但在外壳程序中尝试该 cmdlet 可能会有点令人失望,因为该 cmdlet 看起来不起任何作用。

问题的症结在于 Debug 管道在默认情况下处于关闭状态 — $DebugPreference 变量被设置为“SilentlyContinue”。然而,如果将其设置为“Continue”,则您使用 Write-Debug 发送的任何事物都将在控制台上以黄色文本显示。这是一种将跟踪代码添加到脚本的完美方法,通过该方法,您可完成对复杂脚本的执行。黄色有助于您分辨跟踪和脚本的正常输出,并且可随时关闭调试信息,而无需删除所有 Write-Debug 语句。只需再次设置 $DebugPreference = "SilentlyContinue",即可禁止调试文本。

尽管两个表达式都返回 True,但它们匹配的却是明显不同的对象。幸运的是,有一种方法可查看 –match 运算符实际上需要的内容:每完成一次匹配,无论运算符与 regex 匹配出的字符串中包含什么字符,都使用匹配结果填充称为 $matches 的特殊变量。该 $matches 变量保留其结果,直到使用 –match 运算符完成另一个完全匹配。图 1 显示了我刚才为您讲述的两个表达式的不同之处。您可以看到,\w 匹配“Shell”中的“S”,而重复 \w* 匹配整个单词。

图 1 * 引起的差别

图 1** * 引起的差别 **(单击该图像获得较大视图)

字符组、范围和大小

regex 还可包含字符组或范围(括在方括号中)。例如,[aeiou] 意味着其包含的任何字符(a、e、i 或 u)都是可接受的匹配。[a-zA-Z] 表明 a-z 或 A-Z 范围内的任何字母(即使使用的是不区分大小写的 –match 运算符,仅 a-z 或 A-Z 本身即可)都是可接受的。示例如下:

"Jeff" -match "J[aeiou]ff" (True)
"Jeeeeeeeeeeff" -match "J[aeiou]ff" (False)

您还可使用大括号指定最小和最大字符数。{3} 表明您需要 3 个指定的字符,{3,} 表明至少需要 3 个或者更多指定的字符,{3,4} 表明至少需要 3 个但不超过 4 个指定字符。以下是一种为 IP 地址创建 regex 的理想方法:

"192.168.15.20" -match "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" (True)

该 regex 需要 4 组数字,每组数字包含 1 至 3 个数字,并且数组之间用文字句点隔开。但是请考虑以下示例:

"300.168.15.20" -match "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" (True)

此示例显示了 regex 的限制。虽然该字符串的格式看起来像 IP 地址,但它显然不是一个有效的 IP 地址。regex 无法确定数据是否真正有效;它只能确定数据看起来是否正确。

停止浮动查找

排除 regex 的故障似乎很简单。例如,以下是一个 regex,用于测试格式为 \\Server2\Share 的 UNC 路径:

"\\Server2\Share" -match "\\\\\w+\\\w+" (True)

在此,由于要测试的每个文字反斜杠必须使用另一个反斜杠进行转义,所以 regex 本身很难读取。这看似很正常,但事实并非如此:

"57\\Server2\Share" -match "\\\\\w+\\\w+" (True)

第二个示例很明显(至少在你我看来)是一个无效的 UNC 路径,但 regex 却认为它是有效的 UNC 路径。为什么?请记住:默认情况下,regex 将浮动查找。此 regex 仅仅需要两个反斜杠、一个或多个字母和数字、另一个反斜杠以及更多的字母和数字。该字符串中存在这种模式,但是字符串开头还存在其他数字,这使得该字符串成为无效 UNC。解决办法是告知 regex 在字符串的开头开始匹配,而不能浮动查找。我可以按以下方法操作:

"57\\Server2\Share" -match "^\\\\\w+\\\w+" (False)

^ 字符指示字符串开始的位置。有了这个字符,该无效 UNC 路径将会失败,因为 regex 需要前两个字符为反斜杠,但这种情况并非如此。

同样,$ 符号可用于指示字符串结尾。但此符号对 UNC 路径不会起到很大作用,因为 UNC 路径可能包含其他路径段,如 \\Server2\Share\Folder\File。不过,我确定在很多情况下,您都需要指定字符串结尾。

正则表达式帮助

在 Windows PowerShell 中,about_regular_expressions 帮助主题提供了有关 regex 语言的基本语法帮助,同时在线资源提供了更多相关信息。例如,我最喜欢的网站之一 www.RegExLib.com 提供了公众出于各种目的编写的免费正则表达式库。您可根据关键字进行搜索(如“e-mail”或“UNC”)以快速查找满足需要的 regex,或至少提供一个好的参考。如果您成功创建了一个很棒的 regex,则可将其添加到该库,以便其他人使用它。

此外,我也很喜欢 RegexBuddy (www.RegexBuddy.com)。此工具物美价廉,可提供 regex 图形编辑器。使用 RegexBuddy,可以更轻松地组建复杂的 regex 和对 regex 进行测试,以确保其正确接受有效字符串和拒绝无效字符串。许多其他软件开发人员还创建了免费、共享件以及商业 regex 编辑器和测试程序,这些工具一定会受到用户的青睐。

使用正则表达式

您也许想知道为什么要在现实生活中使用 regex。假设您正在查看 CSV 文件中的信息并要使用该信息在 Active Directory® 中创建新用户。如果该 CSV 文件是由其他人员生成的,则您可能需要证实其中的数据是否正确。regex 非常适合完成此项任务。例如,诸如 \w+ 的简单 regex 可确定名字和姓氏不包含任何特殊字符或符号,而稍复杂的 regex 则可确定电子邮件地址是否符合企业标准。例如,可使用以下 regex:

"^[a-z]+\.[a-z]+@contoso.com$"

此 regex 要求使用 don.jones@contoso.com 形式的电子邮件地址,其中名字和姓氏只能包含字母,并且这些名称必须用句点隔开。顺便提一下,对电子邮件地址字符串编写 regex 最复杂。如果您可将范围缩小至特定的企业标准,编写 regex 可以更简单一些。

不要忘记还有开始和结束定位标记(^ 和 $),它们可确保 contoso.com 之后和构成用户名的字符之前不出现任何字符。

事实上,使用 Windows PowerShell 中的 regex 非常简单。假设变量 $email 包含从 CSV 文件读取的电子邮件地址,可通过以下内容检查该邮件地址是否有效:

$regex = "^[a-z]+\.[a-z]+@contoso.com$"
If ($email –notmatch $regex) {
  Write-Error "Invalid e-mail address $email" 
}

在本示例中,您还认识了一个新的运算符。如果该字符串与所提供的 regex 不匹配,-notmatch 将返回 True(还有一个用于区分大小写比较的 –cnotmatch)。

除了我在此处讲述的内容外,还有许多关于正则表达式的信息,如其他字符类、更高级的操作甚至一个或者两个运算符。此外还有 Windows PowerShell 支持的 [regex] 对象类型。但是,对于入门阶段的学习来说,我在此介绍的有关 regex 语法的简要概述应该足够了。如果需要帮助来解决特别复杂的 regex,可随时通过 www.ScriptingAnswers.com 与我联系。

Don Jones是《TechNet 杂志》**的特约编辑,并且是《Windows PowerShell:TFM》(SAPIEN 出版社)一书的合著者。他主要讲授 Windows PowerShell (www.ScriptingTraining.com),可通过 ScriptingAnswers.com 网站与其联系。

© 2008 Microsoft Corporation 与 CMP Media, LLC.保留所有权利;不得对全文或部分内容进行复制.