我一直在尝试使用PowerShell从.txt文件中的多行提取某些值。
Host
Class
INCLUDE vmware:/?filter=Displayname Equal "server01" OR Displayname Equal "server02" OR Displayname Equal "server03 test"
这就是我想要的:
server01
server02
server03 test
到目前为止,我已经有代码:
$Regex = [Regex]::new("(?<=Equal)(.*)(?=OR")
$Match = $Regex.Match($String)
答案 0 :(得分:3)
您可以使用
[regex]::matches($String, '(?<=Equal\s*")[^"]+')
请参见regex demo。
请参见more ways to extract multiple matches here。但是,您的主要问题是正则表达式模式。 (?<=Equal\s*")[^"]+
模式匹配:
(?<=Equal\s*")
-先于Equal
和0+空格的位置,然后是"
[^"]+
-消耗1个以上的字符(双引号除外)。演示:
$String = "Host`nClass`nINCLUDE vmware:/?filter=Displayname Equal ""server01"" OR Displayname Equal ""server02"" OR Displayname Equal ""server03 test"""
[regex]::matches($String, '(?<=Equal\s*")[^"]+') | Foreach {$_.Value}
输出:
server01
server02
server03 test
这是一个完整的片段,用于读取文件,获取所有匹配项并保存到文件:
$newfile = 'file.txt'
$file = 'newtext.txt'
$regex = '(?<=Equal\s*")[^"]+'
Get-Content $file |
Select-String $regex -AllMatches |
Select-Object -Expand Matches |
ForEach-Object { $_.Value } |
Set-Content $newfile
答案 1 :(得分:2)
另一种选择(PSv3 +),将[regex]::Matches()
与-replace
运算符组合在一起,可获得简洁的解决方案:
$str = @'
Host
Class
INCLUDE vmware:/?filter=Displayname Equal "server01" OR Displayname Equal "server02" OR Displayname Equal "server03 test"
'@
[regex]::Matches($str, '".*?"').Value -replace '"'
正则表达式".*?"
与所有"..."
括起来的令牌匹配; .Value
提取它们,然后-replace '"'
剥离"
个字符。
这可能并不明显,但是根据我的测试,这恰好是这里答案中最快的解决方案-见底部。
顺便说一句:如果-match
运算符(仅查找 a (一个)匹配项)具有一个名为{ {1}},这样一个人就可以写:
-matchall
请参阅GitHub上的this feature suggestion。
从务实的角度来说,这里的所有解决方案都是有帮助的,并且可能足够快,但是在某些情况下,必须优化性能。
通常,使用# WISHFUL THINKING (as of PowerShell Core 6.2)
$str -matchall '".*?"' -replace '"'
(通常是管道)会降低性能-同时提供优雅和内存有效的流处理。
此外,重复调用脚本块(例如,Select-String
)往往很慢-尤其是在使用{ $_.Value }
或ForEach-Object
的管道中,但在较小程度上-使用Where-Object
和.ForEach()
收集方法(PSv4 +)。
在正则表达式领域,您要为变长后向表达式(例如.Where()
)和使用捕获组(例如(?<=EQUAL\s*")
)付出性能损失。
这是使用Time-Command
function进行的性能比较,平均运行1000次:
(.*?)
我的MacBook Pro的采样计时;确切的时间并不重要(您可以删除Time-Command -Count 1e3 { [regex]::Matches($str, '".*?"').Value -replace '"' },
{ [regex]::matches($String, '(?<=Equal\s*")[^"]+') | Foreach {$_.Value} },
{ [regex]::Matches($str, '\"(.*?)\"').Groups.Where({$_.name -eq '1'}).Value },
{ $str | Select-String -Pattern '(?<=Equal\s*")[^"]+' -AllMatches | ForEach-Object{$_.Matches.Value} } |
Format-Table Factor, Command
调用以查看它们),但是相对性能反映在Format-Table
列中,从最快到最慢。
Factor
答案 2 :(得分:1)
您可以修改正则表达式以使用捕获组,该捕获组由括号指示。反斜杠只是转义引号。这样,您就可以捕获所需的内容,然后进行进一步过滤。由于我未提供名称,因此此处的捕获组自动命名为1。捕获组0是包括引号在内的整个匹配项。我切换到Matches方法,因为它包含了字符串的所有匹配项,而Match仅捕获了第一个匹配项。
$regex = [regex]'\"(.*?)\"'
$regex.matches($string).groups.where{$_.name -eq 1}.value
如果要导出结果,可以执行以下操作:
$regex = [regex]'\"(.*?)\"'
$regex.matches($string).groups.where{$_.name -eq 1}.value | sc "c:\temp\export.txt"
答案 3 :(得分:1)
使用Wiktor的优质RegEx直接用Select-String
读取文件的替代方法:
Select-String -Path .\file.txt -Pattern '(?<=Equal\s*")[^"]+' -AllMatches|
ForEach-Object{$_.Matches.Value} | Set-Content NewFile.txt
示例输出:
> Get-Content .\NewFile.txt
server01
server02
server03 test