通过Powershell从文本文件中提取字符串

时间:2019-02-09 13:31:38

标签: regex powershell

我一直在尝试使用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)

4 个答案:

答案 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