RegEx capturing string several lines behind

I have the following sample text that I'm trying to create a capture for.:

object-group network og-n-sna-EWB-UAT-srvrs-4
 network-object host 10.34.68.108
 network-object host 10.34.68.109
 network-object host 10.34.68.110
object-group network og-n-bng-ind-users
 network-object object obj-FAIBLR04L1025
 network-object object obj-FAIBLR04L1741
 network-object object obj-FAIBLR04L1344
 network-object object obj-FAIBLR04L1193
 network-object object obj-FAIBLR06L1318
object-group network og-n-mdm-srvrs-7
 network-object host 10.36.50.101
 network-object host 10.36.50.102

Now, I'm trying to figure out how to capture the name of the object group (og-n-bng-ind-users) that the object obj-FAIBLR04L1344 is a part of. I've tried combining
*\snetwork-object\sobject\sobj-FAIBLR04L1344* with a look behind, but I can't get it to work. Does anyone have any idea on how to construct this regular expression to work with PowerShell?

Upvotes: 0

Views: 90

Answers (4)

Theo
Theo

Reputation: 61058

You could also opt to parse the whole file into an array of PSObjects, which not only makes it easy to get any item you seek, but it also allows you to save the file as CSV file to use in Excel for instance.

$result = switch -Regex -File 'D:\Test\thefile.txt' {
    '^object-group network ' { $group = ($_.Trim() -split '\s')[-1] }
    'network-object' { 
        $item = $_.Trim() -split '\s'
        [PsCustomObject]@{
            Group = $group
            Type  = $item[1]
            Value = $item[2]
        }
    }
}

Once you have this file converted to an object array, you can get anything you need from it, like the object group name for Value 'obj-FAIBLR04L1344'

($result | Where-Object { $_.Value -eq 'obj-FAIBLR04L1344' }).Group  # --> og-n-bng-ind-users

Upvotes: 0

Doug Maurer
Doug Maurer

Reputation: 8868

Another way is to use ConvertFrom-String. This works on your small sample data. It's quite possible there needs to be more "training" data for the template. The technology it's built on is amazing.

$template = @'
object-group network og-n-sna-EWB-UAT-srvrs-4
 network-object host 10.34.68.108
 network-object host 10.34.68.109
 network-object host 10.34.68.110
object-group network {Name: og-n-bng-ind-users}
 network-object object obj-FAIBLR04L1025
 network-object object obj-FAIBLR04L1741
 network-object object obj-FAIBLR04L1344
 network-object object obj-FAIBLR04L1193
 network-object object obj-FAIBLR06L1318
object-group network og-n-mdm-srvrs-7
 network-object host 10.36.50.101
 network-object host 10.36.50.102
'@

@'
object-group network og-n-sna-EWB-UAT-srvrs-4
 network-object host 10.34.68.108
 network-object host 10.34.68.109
 network-object host 10.34.68.110
object-group network og-n-bng-ind-users
 network-object object obj-FAIBLR04L1025
 network-object object obj-FAIBLR04L1741
 network-object object obj-FAIBLR04L1344
 network-object object obj-FAIBLR04L1193
 network-object object obj-FAIBLR06L1318
object-group network og-n-mdm-srvrs-7
 network-object host 10.36.50.101
 network-object host 10.36.50.102
'@ | ConvertFrom-String -TemplateContent $template

Or from a file

Get-Content $file | ConvertFrom-String -TemplateContent $template

Name              
----              
og-n-bng-ind-users

https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/convertfrom-string?view=powershell-5.1

Upvotes: 0

Hernán Alarcón
Hernán Alarcón

Reputation: 4099

Assuming the text is stored in text.txt, I believe this could work:

Get-Content -Raw text.txt |
  Select-String `
    -AllMatches `
    -Pattern '(?<=object-group network ).+(?=(\s*network-object object .*)*\s*network-object object obj-FAIBLR04L1344)' |
  foreach {$_.Matches.Value}

Get-Content -Raw passes the content of text.txt as a single object (multiline string) to Select-String. The -AllMatches option shows all the matches instead of just the first one (in case the object is in several groups in your example text). The -Pattern option receives the pattern to search. This pattern uses a lookbehind for object-group network and a lookahead for zero or more \s*network-object object .* followed by \s*network-object object obj-FAIBLR04L1344.

Select-String prints matching lines (the whole text in this case). To avoid this, foreach {$_.Matches.Value} iterates on the matches and prints its value.

Upvotes: 1

filimonic
filimonic

Reputation: 4634

Are you sure you want RegEx here?

Prepare:

$text = @'
object-group network og-n-sna-EWB-UAT-srvrs-4
 network-object host 10.34.68.108
 network-object host 10.34.68.109
 network-object host 10.34.68.110
object-group network og-n-bng-ind-users
 network-object object obj-FAIBLR04L1025
 network-object object obj-FAIBLR04L1741
 network-object object obj-FAIBLR04L1344
 network-object object obj-FAIBLR04L1193
 network-object object obj-FAIBLR06L1318
object-group network og-n-mdm-srvrs-7
 network-object host 10.36.50.101
 network-object host 10.36.50.102
'@

$networkObjectId = 'obj-FAIBLR04L1344' + [System.Environment]::NewLine

Variant:

$objectGroupListFound = @($text.Split([string[]]@('object-group'), [System.StringSplitOptions]::RemoveEmptyEntries) |
    Where-Object { $_.Contains( "network-object object $($networkObjectId)" ) } |
    ForEach-Object { $_.Split([System.Environment]::NewLine)[0]} |
    ForEach-Object { $_.Split()[-1]} )

if ($objectGroupListFound.Count -ne 1) 
{
    Write-Error "No objectGroup found for networkObjectId $($networkObjectId) or found more than one. Found $($objectGroupListFound.Count)." 
}
## There I omit "else", assuming code upper is wrapped into try block and on error, it will not reach this line.
$objectGroup = $objectGroupListFound[0]

Variant:

$tempText = $text.Substring(0,$text.IndexOf($networkObjectId))
$tempText = $tempText.Substring($tempText.LastIndexOf('object-group'))
$objectGroup = $tempText.Split("`r`n".ToCharArray(), [System.StringSplitOptions]::RemoveEmptyEntries)[0].Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[2]

Variant (line by line) - I think it is most simple and clear

$text = [System.IO.File]::ReadAllLines($path)
# or  $text = $text.Split([char[]]"`r`n", [System.StringSplitOptions]::RemoveEmptyEntries)

$networkObjectId = "obj-FAIBLR04L1344"

$objectGroupCache = $null
$objectGroup = $null
for ($i = 0; $i -lt $text.Count; $i++)
{
    if ($text[$i].Trim().StartsWith('object-group network'))
    {
        # We are entering this group.
        $objectGroupCache =$text[$i].Split([char[]]" `t", [System.StringSplitOptions]::RemoveEmptyEntries)[-1]
    }
    elseif ($text[$i].Trim().StartsWith('network-object') -and $text[$i].Trim().EndsWith($networkObjectId))
    {
        $objectGroup = $objectGroupCache
        break;
    }
}

if ([String]::IsNullOrWhiteSpace($objectGroup))
{
    Write-Error "Not found" 
}

Upvotes: 0

Related Questions