2

I'm trying to remove items from an array that match special keywords. My array looks something like this:

$Printers =@('Printer Phone', 'Printer Building1', 'Printer XML', 'Printer Station', ...) 

I want to remove all entries that match parts of certain strings, like filtering out every item that has "Phone" or "XML" in it's value. I thought of something like this but I'm not quiet sure if I'm on the right track:

$contains = @('Phone', 'XML')
$Printers -Filter * | Where-Object { $contains -contains $_.name }

Is there a simple way to achieve this?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
pyte
  • 41
  • 1
  • 5

3 Answers3

3

Here is one way to do this.

> $contains = @('Phone', 'XML')
> $Printers = @('Printer Phone','Printer Building1','Printer XML', 'Printer Station')
> $Printers | Where-Object { $_ -Match ($contains -Join "|") }
Printer Phone
Printer XML
> $Printers | Where-Object { $_ -notMatch ($contains -Join "|") }
Printer Building1
Printer Station
Abdul Niyas P M
  • 18,035
  • 2
  • 25
  • 46
  • It's an elegant solution, but it would help if you explained it a bit and mentioned the potential need to apply `[regex]::Escape()` to the search terms (not necessary with the sample input). Also, it would help if you allowed readers to copy and paste the solution as-is in order to try it, in a single operation, without having to remove the `> ` prefixes or having to copy and paste the relevant part of each line individually. – mklement0 Sep 06 '21 at 14:10
3

Three remarks

  • You don't need @() to make arrays. The comma is what makes arrays, the @() is redundant here.
  • The PowerShell -contains operator does not check whether a string contains a substring. It checks whether an array contains a value.
    Using the .NET .Contains() method works for substring checks, but be aware, it's case-sensitive. Lower-case your strings if you need case-insensitive comparisons (good enough and easier than doing it "by the book").
  • Any value that you produce in a {} script block and don't assign to a variable, will become part of that script block's output. That's how the Where-Object block below works.

Code:

$Printers = 'Printer Phone','Printer Building1','Printer XML', 'Printer Station' 
$contains = 'Phone','XML'

$Printers | Where-Object { 
    $printer = $_
    $contains | Where-Object {  $printer.Contains($_) }
}

Where-Object considers non-empty output as $true. So filtering $contains down to something (or nothing) will decide whether the $Printers value makes it. The above prints

Printer Phone
Printer XML

You could use -match, but then you would have to start worrying about regex-escaping your $contains values properly to avoid run-time errors.

Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • Just as a matter of practice, it's *always* a good idea to add @() around your arrays, even if it looks redundant. Arrays with one element otherwise easily get turned into single items instead of arrays. For example, `(GetList) + "XML"` is very different than `@(GetList) + "XML"`, if the GetList function returns an array of only one element. – John Doggett Jan 25 '23 at 00:45
2

You can have an array of patterns with select-string. But select-string's output is a matchinfo object (unless it's converted to a string) and the line property has the original string examined.

$patterns = echo Phone XML
$printers = 'Printer Phone', 'Printer Building1', 'Printer XML',
  'Printer Station'
$printers | select-string $patterns | % line
Printer Phone
Printer XML

Unfortunately, -contains is a frequent point of confusion.

'printer station' -contains 'station'

False


-split 'printer station' -contains 'station'

True
js2010
  • 23,033
  • 6
  • 64
  • 66
  • Nice, though it's safer to add `-SimpleMatch` in this case, for _literal_ substring searches. In PowerShell (Core) 7+, `-Raw` is now available for direct output of the matching lines as strings; e.g. `$printers | select-string -Raw -SimpleMatch $patterns` – mklement0 Sep 06 '21 at 21:28