32

我在尝试匹配文件中的某个配置块(多个)时遇到了一些问题。下面是我试图从配置文件中提取的块:

ap71xx 00-01-23-45-67-89
 use profile PROFILE
 use rf-domain DOMAIN
 hostname ACCESSPOINT
 area inside
!

像这样的有多个,每个都有不同的 MAC 地址。如何跨多行匹配配置块?

4

4 回答 4

62

您可能遇到的第一个问题是,为了跨多行匹配,您需要将文件的内容作为单个字符串而不是单独的行来处理。例如,如果您使用 Get-Content 来读取文件的内容,那么默认情况下它将为您提供一个字符串数组 - 每行一个元素。要跨行匹配,您希望文件包含在单个字符串中(并希望文件不会太大)。你可以这样做:

$fileContent = [io.file]::ReadAllText("C:\file.txt")

或者在 PowerShell 3.0 中,您可以将 Get-Content 与-Raw参数一起使用:

$fileContent = Get-Content c:\file.txt -Raw

然后你需要指定一个正则表达式选项来匹配跨行终止符,即

  • SingleLine 模式(.匹配任何字符,包括换行),以及
  • 多行模式(^$匹配嵌入式行终止符),例如
  • (?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-Stringcmdlet 进行搜索,因为您可以指定-AllMatches并且它将输出所有匹配项,而-match运算符在第一个匹配项后停止。有意义,因为它是一个布尔运算符,只需要确定是否存在匹配项。

于 2012-09-24T21:48:35.190 回答
6

如果这对某人仍然有价值并且根据实际要求,基思答案中的正则表达式不需要那么复杂。如果用户只是想输出每个块,则以下内容就足够了:

$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 }
于 2015-05-11T02:52:01.743 回答
2

此正则表达式将搜索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
于 2012-09-24T22:02:08.083 回答
0

这是我的看法。如果不需要正则表达式,可以使用 -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 
于 2019-07-04T15:17:45.963 回答