Powershell:类似运算符的神秘行为

Powershell: enigmatic behavior of -like operator

我们有一个保存服务器数据库的应用程序 - 服务器名称和其他相关信息的列表。有时我们需要将信息导出为XML格式,以便通过Powershell脚本进行处理。 XML 文件中的服务器名称可以采用简单 ("ServerXX") 或 FQDN ("ServerXX.abc.com") 格式。该脚本搜索始终为简单格式的服务器名称,搜索结果应包含与搜索名称匹配的所有简单和完整服务器名称。

主要搜索运算符(稍微简化)如下所示:

$FoundServer = ($ServerList | Where {$_.Name -match $ServerName+"*"})

$ServerList 这里是字符串数组(服务器名称)。看起来很简单并且按预期工作。通常。

奇怪的是,有时脚本找不到某些 FQDN。例如,如果文件中的 FQDN 是 "ServerXX.abc.com",而我们正在搜索 "ServerXX",则找不到 FQDN。同时搜索其他名称按预期工作。在调试脚本的时候,可以看到{}里面的表达式就是字面上的"ServerXX.abc.com" -like "ServerXX*"。一定是真的。但是得到的搜索结果是空的。更有趣的是,如果将搜索名称指定为 "ServerXX."、"ServerXX.a" 或 FQDN 中的其他字母,脚本会找到它。如果在没有域名的文件中指定相同的服务器名称(以简单形式),脚本会找到它。

好吧,更神秘的是,我们有两个已安装应用程序的实例,一个用于生产,另一个用于测试。测试版包含一个小得多的服务器数据库。如果我将 prod 实例中的 "invisible" 服务器名称添加到测试实例并导出数据库,脚本会毫无问题地找到该名称。

如果我将 -like 替换为 -match,问题就会消失。所以这不是 XML 文件生成器的问题(它是另一个 PS 脚本生成 PSCustomObject 并通过 Export-CliXml 导出它)。这也不是服务器名称中某些不可见或非 ANSI 符号的问题。我还手动检查了 XML 文件的内容。它很大(几十兆字节)而且很复杂,所以很难分析,但我没有发现任何可见的问题。 XML 结构看起来正确。

我不明白这种随机行为。它会以某种方式与 XML 文件大小有关吗? PS 或类似的内存不足?我们使用 Powershell v4.

请注意,此答案不是解决方案,因为(截至撰写本文时)没有足够的信息来诊断您的问题;但是,您 使用 -like-match 运算符 值得仔细研究。


$_.Name -match $ServerName+"*"(更简洁:$_.Name -match "$ServerName*")与相同 $_.Name -like "$ServerName*":

  • -match 使用 regular expressions(正则表达式),(也)匹配 输入 的部分 ,除非明确制定匹配输入的开头 (^) and/or 结尾 ($)。

  • -like 使用 wildcard expressions,它必须 匹配整个输入 .

虽然正则表达式和通配符关系很远,但它们的 语法和功能不同;正则表达式更强大;在手头的情况下(请注意,默认情况下匹配不区分大小写):

  • ... -like 'ServerXX*' 匹配 ServerXX 开头并后跟 零个或多个 任意字符(*).

    • 输入 'ServerXX''ServerXX.foo.bar''ServerXXY' 都将 return $true.
  • ... -match 'ServerXX*' 匹配包含子字符串 ServerX 的字符串(只是 one X!) 在输入的任何地方,如果后跟零个或多个*X个字符,因为重复符号*修饰前面的 character/subexpression.

    • 虽然输入 'ServerXX''ServerXX.foo.bar' 会 return $true,但 'ServerX''fooServerXX' 也会如此 - 在这种情况下这是不希望的.

如果您输入的是 FQDN,请使用以下任一等效表达式:

... -like 'ServerXX.*'

... -match '^ServerXX\.'

如果服务器名称是通过 变量 提供的,例如$ServerName,使用"...",一个expandable string,最简单的情况:

... -like "$ServerName.*"

... -match "^$ServerName\."

这在 服务器名称 的情况下很好,因为它们不允许包含可能被错误解释为正则表达式/通配符的字符 元字符(有特殊意义的字符,如*)。

通常,最安全的方法是显式转义一个变量值以确保其文字 使用,但请注意正则表达式 中比在通配符表达式[=112] 中更可能需要这样做=],因为正则表达式有更多的元字符:

... -like ('{0}.*' -f [System.Management.Automation.WildcardPattern]::Escape($ServerName))


... -match ('^{0}\.' -f [regex]::Escape($ServerName))

使用带有 -f 单引号 模板字符串,format operator{0} 表示第一个 RHS 操作数),使其成为很明显哪些部分按字面意思使用,哪些部分拼接为 escaped 变量值。