1

I have an array and use select-string to find duplicates.

$2 = $arraySNOW | Select-String -Pattern $request

The process usually finds 2 items and but each item is now rapped inside @{} object and I can not access the value for a specific property

$2[0]

@{number=63887630; request=abc560vi}

$2 | Get-Member
TypeName: Microsoft.PowerShell.Commands.MatchInfo

$2.gettype()
IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array

How to pull the value of the request for both items in $2?

Dijkgraaf
  • 11,049
  • 17
  • 42
  • 54
  • 1
    `Select-String` as the name implies is for strings not objects.... `$arraySNOW | Where-Object Request -Match $request` – Santiago Squarzon Aug 16 '23 at 22:32
  • Hi Santiago, why the variable type shows that it is Object[] and System.Array? – Tihomir Buncic Aug 16 '23 at 22:34
  • 1
    you are right - the property Line is a string! $2[0].line.gettype() IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True String System.Object Thank you! – Tihomir Buncic Aug 16 '23 at 22:41
  • If you get only one value PS will not make it an array. To force an array use : $2 = @($arraySNOW | Select-String -Pattern $request) – jdweng Aug 16 '23 at 22:51
  • 1
    @Dijkgraaf has done it for this post, but please [format your posts properly](https://stackoverflow.com/help/formatting) in the future. – mklement0 Aug 17 '23 at 01:40

2 Answers2

1

Santiago Squarzon has provided the crucial pointer:

Select-String is for string searches, whereas you want OO techniques to filter your objects, so you should use Where-Object to filter your objects by a property value of interest:

# Filters the input objects by whether their .Request property
# matches the specified regex pattern.
$arraySNOW | Where-Object Request -match $request

The above passes the matching input objects through, so you can access their properties as needed; e.g., to get the .number property values of the matching objects:

$arraySNOW | Where-Object request -match $request |
  ForEach-Object number  # alternatively: Select-Object -ExpandProperty number

Or, more succinctly, using member-access enumeration:

($arraySNOW | Where-Object request -match $request).number

As for what you tried:

$arraySNOW | Select-String -Pattern $request returns any number of Microsoft.PowerShell.Commands.MatchInfo instances describing the string matches performed on the input objects.

  • Their .Line property contains the text of the input line or stringified input object (see below) that was matched; in PowerShell (Core) 7+, you can obtain this text directly with the -Raw switch.

If Select-Object emits two ore more objects and you capture them in a variables ($2 = ...) PowerShell automatically collects them in an [object[]] (System.Object[]) array for you.

  • Piping an array (a list-like enumerable) to Get-Member invariably results in the array's enumeration, so that Get-Member reports the distinct types among its elements - hence the report of type Microsoft.PowerShell.Commands.MatchInfo

  • By contrast, calling .GetType() on any object - whether it is a scalar or an array / list / collection - reports its own type.

The for-display representation of a MatchInfo instance you saw with $2[0] is in essence the value of its .Line property:

  • Non-string input to Select-Object is implicitly stringified, and the stringified value is both used for matching and reported via the .Line property.

  • Therefore, you lose the object identity of the input, and simply get a string representation of it.

    • The specific form of the string representation you saw - @{number=63887630; request=abc560vi} resembles PowerShell hashtable literals - but is, in fact, how [pscustomobject] instances are stringified - see this answer for details.

    • With many other types, the implicit stringification results in useless representations, namely simply the full type name of the input object, given that .ToString() stringification (with some custom functionality overlaid) is applied (e.g., stringifying a hashtable results in string 'System.Collections.Hashtable'; try @{ foo = 1 }.ToString())

    • Given that PowerShell has a rich for-display output-formatting system that provides much more helpful representations of most types, it is unfortunate that Select-String doesn't apply PowerShell's formatting implicitly; see GitHub issue #10726 for a discussion.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • Thank you very much for the explanation!. I was avoiding Where-Object because I am searching reports with around 20K lines. I thought that select-string would preform better. – Tihomir Buncic Aug 17 '23 at 18:48
  • Glad to hear it helped, @TihomirBuncic. For better performance, you can use the [intrinsic `.Where()` method](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_Arrays#where), and using a [`foreach`](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_Foreach) loop with an `if` statement may perform best. – mklement0 Aug 17 '23 at 20:32
0

What you are likely looking for is the "Line" Property.

Here is a small example:

$arraySNOW = @("One Number", "A Name", "Another Number")
$request = "number"

$2 = $arraySNOW | Select-String -Pattern $request 
$2 | % { $_.Line }

Outputs the two lines which matched:

One Number
Another Number

You can also just write $2.Line instead of $2 | % { $_.Line } which will effectively do the same thing for you. PowerShell is smart! But I find it a bit more terse for someone reading your script and they may not realize you are doing an operation for each element of an array.

Tolga
  • 2,643
  • 1
  • 27
  • 18