我们有一个应用程序来保存服务器数据库 - 服务器名称列表和其他相关信息。有时,我们需要以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实例中的“不可见”服务器名称添加到测试实例并导出数据库,则脚本会发现此名称没有任何问题。
如果我用-match替换-like,问题就会消失。所以这不是XML文件生成器的问题(它是另一个生成PSCustomObject并通过Export-CliXml导出它的PS脚本)。它也不是服务器名称中某些不可见或非ANSI符号的问题。我还手动检查了XML文件的内容。它是巨大的(几十兆字节)和复杂的,所以它很难分析,但我没有发现任何明显的问题。 XML结构看起来是正确的。
我不明白随机行为。是否可以以某种方式与XML文件大小相关?内存缺乏PS或类似的东西?我们使用Powershell v4。
请注意,这个答案不是解决方案,因为(截至本文撰写时)没有足够的信息来诊断您的问题;但是,你对-like
和-match
运算符的使用值得仔细研究。
$_.Name -match $ServerName+"*"
(更简洁:$_.Name -match "$ServerName*"
)与$_.Name -like "$ServerName*"
不同:
-match
使用regular expressions(正则表达式),它(也)匹配输入的一部分,除非明确表示在输入的开头(^
)和/或结束($
)匹配。-like
使用wildcard expressions,它必须与输入整体匹配。虽然正则表达式和通配符有很大关系,但它们的语法和功能却不同;正则表达式更强大;在手边的情况下(请注意,匹配在默认情况下不区分大小写):
... -like 'ServerXX*'
匹配以ServerXX
开头的字符串,后跟零个或多个任意字符(*
)。
输入'ServerXX'
,'ServerXX.foo.bar'
和'ServerXXY'
都将返回$true
。... -match 'ServerXX*'
匹配包含子串ServerX
(只有一个X
!)的字符串,如果后面跟着零或更多(*
)X
字符,因为重复符号*
修改前面的字符/子表达式。
虽然输入'ServerXX'
和'ServerXX.foo.bar'
将返回$true
,'ServerX'
和'fooServerXX'
也是如此 - 在这种情况下这是不受欢迎的。如果您的输入是FQDN,请使用以下表达式中的任何一个,它们是等效的:
... -like 'ServerXX.*'
... -match '^ServerXX\.'
如果服务器名称是通过变量提供的,例如$ServerName
,在最简单的情况下使用"..."
,expandable string:
... -like "$ServerName.*"
... -match "^$ServerName\."
这在服务器名称的情况下很好,因为它们不允许包含可能被错误地解释为正则表达式/通配符元字符的字符(具有特殊含义的字符,例如*
)。
通常,最安全的方法是显式转义变量值以确保其字面用法,但请注意,在正则表达式中比在通配符表达式中更需要这样做,因为正则表达式具有更多元字符:
... -like ('{0}.*' -f [System.Management.Automation.WildcardPattern]::Escape($ServerName))
... -match ('^{0}\.' -f [regex]::Escape($ServerName))
使用带有-f
的单引号模板字符串,format operator({0}
代表第一个RHS操作数),明确表示哪些部分是字面上使用的,哪些部分是作为转义变量值拼接而成的。