Home > Blockchain >  Accessing matches variables in Powershell
Accessing matches variables in Powershell

Time:10-28

This question is more about my understanding Powershell's objects rather than solving this practical example. I know there are other ways of separating out a page number from a string.

In my example I want to do this by accessing the object-match-value of the piped pattern match.

# data
$headerString = 'BARTLETT-BEDGGOOD__PAGE_5 BEECH-BEST__PAGE_6'
# require the number of page only
$regexPageNum = '([0-9]$)'

# split the header string into two separate strings to access page numbers
[string[]]$pages = $null
$pages = $headerString -split ' '

# access page numbers using regex pattern 
$pages[0] | Select-String -AllMatches -Pattern $regexPageNum | Select-Object {$_.Matches.Value}

The output is:

$_.Matches.Value
----------------
5

Okay. So far so good. I see the page number of array member pages[0] But how do I take this value from the object? The following does not work.

$x = $pages[0] | Select-String -AllMatches -Pattern $regexPageNum | Select-Object {$_.Matches.Value}
Write-Host "Here it is:"$x

Output:

Here it is: @{$_.Matches.Value=5}

Instead of assigning the value 5 to the variable $x Powershell assigns, what looks to me: a hash table with an object description as its only member?

But if I try to access my variable using "Brackets for Access" Reference: hashtables Powershell indicates that variable $x is in fact an array.

x = $pages[0] | Select-String -AllMatches -Pattern $regexPageNum | Select-Object {$_.Matches.Value}
Write-Host "Here it is:"$x
$y = $x[$_.Matches.Value]
Write-Host "What about now:"$y

Output:

Here it is: @{$_.Matches.Value=5}
InvalidOperation: 

Line |
  33 |  $y = $x[$_.Matches.Value]
     |  ~~~~~~~~~~~~~~~~~~~~~~~~~
     | Index operation failed; the array index evaluated to null.

What about now:

Okay. At this stage I know I'm being silly. But the point I'm trying to make is: How can I retrieve the value I want when I'm done with the Powershell object?

CodePudding user response:

You can use $x.{ $_.Matches.Value } to access the value.

$x = $pages[0] | Select-String -AllMatches -Pattern $regexPageNum | Select-Object { $_.Matches.Value }
$x.{ $_.Matches.Value } # This will print 5

ie, You would have to wrap the property name inside {} since the property name contains "."

Instead of this way, I would suggest you to create a calculated property using Select-Object which makes the code more readable.

$x = $pages[0] | Select-String -AllMatches -Pattern $regexPageNum | Select-Object @{Name = 'PageNumber'; Expression = {$_.Matches.Value}}
$x.PageNumber

CodePudding user response:

#Access matches in case of single match
$x = "red blue yellow green" | select-string -Pattern 'blue'
$x.matches.value
#Output
blue

#Access matches in case of multi match
$x = "red blue yellow green blue" | select-string -Pattern 'blue' -AllMatches
$x.matches.value
#Output
blue
blue

CodePudding user response:

When you use a scriptblock as a parameter to Select-Object the return value will contain a property whose name matches the source code of the script block...

PS> @{ "aaa" = "bbb" } | select-object { $_.aaa; <# xxx #> }

$_.aaa; <# xxx #>
-------------------
bbb

In this pathological case, if I want to access the property I can't use the name in the default "dotted" notation because it contains reserved characters, but you can access it if you quote the property name:

PS> $x = @{ "aaa" = "bbb" } | select-object { $_.aaa; <# xxx #> }

# note the leading and trailing spaces in the string because the
# the original scriptblock source contains spaces between the "{" and "}"
PS> $x.' $_.aaa; <# xxx #> '
bbb

In your case you'd do this:

PS> $x = $pages[0] | Select-String -AllMatches -Pattern $regexPageNum | Select-Object {$_.Matches.Value}

PS C:\Users\Mike> $x.'$_.Matches.Value'

Other options work too:

$x = $pages[0] `
    | Select-String -AllMatches -Pattern $regexPageNum `
    | Select-Object {$_.Matches.Value}

# get the property whose name is contained in the $name variable
PS> $name = '$_.Matches.Value'
PS> $x.$name
5

# the scriptblock gets converted into a string, and then that string
# is used as a property name
PS> $x.{$_.Matches.Value}
5

# note the whitespace in both scriptblocks has to match *exactly* otherwise the property name won't be found
PS> $x.{ $_.Matches.Value }
ParentContainsErrorRecordException: The property ' $_.Matches.Value ' cannot be found on this object. Verify that the property exists.

but...

There's an easier way - if you pass a hashtable to Select-Object instead of a scriptblock you can specify the name of the property - e.g.

PS> $x = $pages[0] `
    | Select-String -AllMatches -Pattern $regexPageNum `
    | Select-Object @{ "l"="Count"; "e"={$_.Matches.Value} }

PS> $x

Count
-----
5

PS> $x.Count
5

References:

  • Related