我有一个数组$ vhdlist,其内容类似于以下文件名:
UVHD-S-1-5-21-8746256374-654813465-374012747-4533.vhdx
UVHD-S-1-5-21-8746256374-654813465-374012747-6175.vhdx
UVHD-S-1-5-21-8746256374-654813465-374012747-8147.vhdx
UVHD-template.vhdx
我想使用正则表达式,并留下一个只包含文件名的SID部分的数组。
我使用以下内容:
$sids = foreach ($file in $vhdlist)
{
[regex]::split($file, '^UVHD-(?:([(\d)(\w)-]+)).vhdx$')
}
这有两个问题:在结果数组中,每个SID有3个空行;和"模板"文件名匹配(输出中的结果行只是"模板")。如何获得一组SID作为输出,而不包括"模板"线?
答案 0 :(得分:3)
您似乎希望过滤列表到包含SID的文件名。使用Where-Object
完成过滤(简称where
);你不需要循环。
对于这个简单的情况,SID可以被描述为" S-
,然后是一堆数字和短划线" 。这使我们的文件名为^UVHD-S-[\d-]*\.vhdx$
。
结合我们得到:
$vhdlist | where { $_ -Match "^UVHD-S-[\d-]*\.vhdx$" }
如果您确实没有字符串数组,但实际上是一组文件,请直接使用它们。
dir C:\some\folder | where { $_.Name -Match "^UVHD-S-[\d-]*\.vhdx$" }
或者,您甚至可以将其简单化为:
dir C:\some\folder\UVHD-S-*.vhdx
修改
从字符串列表中提取SID可以被视为组合的转换(对于每个元素,提取SID)和过滤器(删除不匹配)操作
PowerShell的ForEach-Object
cmdlet(简称foreach
)与其他语言中的map()
类似。它接受每个输入元素并返回一个新值。实际上,它将输入元素列表转换为输出元素。与-replace
运算符一起,您可以通过这种方式提取SID。
$vhdlist | foreach { $_ -replace ^(?:UVHD-(S-[\d-]*)\.vhdx|.*)$,"`$1" } | where { $_ -gt "" }
.NET语言的正则表达式反向引用是$1
。 $
是PowerShell字符串中的特殊字符,因此除非没有歧义,否则需要对其进行转义。反引号是PS转义字符。你也可以逃避正则表达式中的$
,但是没有必要。
作为最后一步,我们使用where
删除空字符串(即不匹配)。这样做意味着我们只需要应用一次正则表达式,而不是在首先过滤并替换第二次时应用两次。
PowerShell运算符也可以直接处理列表。所以甚至可以缩短上述内容:
$vhdlist -replace "^UVHD-(S-[\d-]*)\.vhdx$","`$1" | where { $_ -gt "" }
较短版本仅适用于在调用.ToString()
时生成正确内容的实际字符串或对象的列表。
正则表达式细分:
^ # start-of-string anchor (?: # begin non-capturing group (either...) UVHD- # 'UVHD-' ( # begin group 1 S-[\d-]* # 'S-' and however many digits and dashes ) # end group 1 \.vhdx # '.vhdx' | # ...or... .* # anything else ) # end non-capturing group $ # end-of-string anchor