我在尝试匹配文件中的某个配置块(多个)时遇到了一些问题。下面是我试图从配置文件中提取的块:
ap71xx 00-01-23-45-67-89
use profile PROFILE
use rf-domain DOMAIN
hostname ACCESSPOINT
area inside
!
像这样的有多个,每个都有不同的 MAC 地址。如何跨多行匹配配置块?
我在尝试匹配文件中的某个配置块(多个)时遇到了一些问题。下面是我试图从配置文件中提取的块:
ap71xx 00-01-23-45-67-89
use profile PROFILE
use rf-domain DOMAIN
hostname ACCESSPOINT
area inside
!
像这样的有多个,每个都有不同的 MAC 地址。如何跨多行匹配配置块?
您可能遇到的第一个问题是,为了跨多行匹配,您需要将文件的内容作为单个字符串而不是单独的行来处理。例如,如果您使用 Get-Content 来读取文件的内容,那么默认情况下它将为您提供一个字符串数组 - 每行一个元素。要跨行匹配,您希望文件包含在单个字符串中(并希望文件不会太大)。你可以这样做:
$fileContent = [io.file]::ReadAllText("C:\file.txt")
或者在 PowerShell 3.0 中,您可以将 Get-Content 与-Raw
参数一起使用:
$fileContent = Get-Content c:\file.txt -Raw
然后你需要指定一个正则表达式选项来匹配跨行终止符,即
.
匹配任何字符,包括换行),以及^
并$
匹配嵌入式行终止符),例如(?smi)
- 注意“i”是忽略大小写例如:
C:\> $fileContent | Select-String '(?smi)([0-9a-f]{2}(-|\s*$)){6}.*?!' -AllMatches |
Foreach {$_.Matches} | Foreach {$_.Value}
00-01-23-45-67-89
use profile PROFILE
use rf-domain DOMAIN
hostname ACCESSPOINT
area inside
!
00-01-23-45-67-89
use profile PROFILE
use rf-domain DOMAIN
hostname ACCESSPOINT
area inside
!
使用Select-String
cmdlet 进行搜索,因为您可以指定-AllMatches
并且它将输出所有匹配项,而-match
运算符在第一个匹配项后停止。有意义,因为它是一个布尔运算符,只需要确定是否存在匹配项。
如果这对某人仍然有价值并且根据实际要求,基思答案中的正则表达式不需要那么复杂。如果用户只是想输出每个块,则以下内容就足够了:
$fileContent = [io.file]::ReadAllText("c:\file.txt")
$fileContent |
Select-String '(?smi)ap71xx[^!]+!' -AllMatches |
%{ $_.Matches } |
%{ $_.Value }
正则表达式的ap71xx[^!]*!
性能会更好,.*
不建议在正则表达式中使用,因为它会产生意想不到的结果。该模式[^!]+!
将匹配除感叹号外的任何字符,后跟感叹号。
如果输出中不需要块的开头,则更新后的脚本为:
$fileContent |
Select-String '(?smi)ap71xx([^!]+!)' -AllMatches |
%{ $_.Matches } |
%{ $_.Groups[1] } |
%{ $_.Value }
Groups[0]
包含整个匹配的字符串,Groups[1]
将在正则表达式的括号内包含字符串匹配。
如果$fileContent
不需要任何进一步的处理,则可以消除该变量:
[io.file]::ReadAllText("c:\file.txt") |
Select-String '(?smi)ap71xx([^!]+!)' -AllMatches |
%{ $_.Matches } |
%{ $_.Groups[1] } |
%{ $_.Value }
此正则表达式将搜索ap
后跟任意数量的字符和以 a 结尾的新行的文本!
:
(?si)(a).+?\!{1}
所以我有点无聊。我编写了一个脚本,它将按照您的描述分解文本文件(只要它只包含您显示的行)。它可以与其他随机行一起使用,只要它们不包含关键字:ap、profile、domain、hostname 或 area。它将导入它们,并逐行检查每个属性(MAC、配置文件、域、主机名、区域),并将它们放入以后可以使用的对象中。我知道这不是您要求的,但是由于我花时间研究它,希望它可以用于一些好处。如果有人感兴趣,这是脚本。它需要根据您的特定需求进行调整:
$Lines = Get-Content "c:\test\test.txt"
$varObjs = @()
for ($num = 0; $num -lt $lines.Count; $num =$varLast ) {
#Checks to make sure the line isn't blank or a !. If it is, it skips to next line
if ($Lines[$num] -match "!") {
$varLast++
continue
}
if (([regex]::Match($Lines[$num],"^\s.*$")).success) {
$varLast++
continue
}
$Index = [array]::IndexOf($lines, $lines[$num])
$b=0
$varObj = New-Object System.Object
while ($Lines[$num + $b] -notmatch "!" ) {
#Checks line by line to see what it matches, adds to the $varObj when it finds what it wants.
if ($Lines[$num + $b] -match "ap") { $varObj | Add-Member -MemberType NoteProperty -Name Mac -Value $([regex]::Split($lines[$num + $b],"\s"))[1] }
if ($lines[$num + $b] -match "profile") { $varObj | Add-Member -MemberType NoteProperty -Name Profile -Value $([regex]::Split($lines[$num + $b],"\s"))[3] }
if ($Lines[$num + $b] -match "domain") { $varObj | Add-Member -MemberType NoteProperty -Name rf-domain -Value $([regex]::Split($lines[$num + $b],"\s"))[3] }
if ($Lines[$num + $b] -match "hostname") { $varObj | Add-Member -MemberType NoteProperty -Name hostname -Value $([regex]::Split($lines[$num + $b],"\s"))[2] }
if ($Lines[$num + $b] -match "area") { $varObj | Add-Member -MemberType NoteProperty -Name area -Value $([regex]::Split($lines[$num + $b],"\s"))[2] }
$b ++
} #end While
#Adds the $varObj to $varObjs for future use
$varObjs += $varObj
$varLast = ($b + $Index) + 2
}#End for ($num = 0; $num -lt $lines.Count; $num = $varLast)
#displays the $varObjs
$varObjs
这是我的看法。如果不需要正则表达式,可以使用 -like 或 .contains()。这个问题从来没有说搜索模式是什么。这是一个带有 Windows 文本文件的示例。
$file = (get-content -raw file.txt) -replace "`r" # avoid the line ending issue
$pattern = 'two
three
f.*' -replace "`r"
# just showing what they really are
$file -replace "`r",'\r' -replace "`n",'\n'
$pattern -replace "`r",'\r' -replace "`n",'\n'
$file -match $pattern
$file | select-string $pattern -quiet