HorstSchlemmer12345
HorstSchlemmer12345

Reputation: 11

PowerShell -notlike not working in Where-Object

I'm struggling with this code:

Get-Sensor -Name Ping | where {$_.Tags -like "Kat.A" -or $_.Tags -like "Kat.B" -or $_.Tags -like "Kat.C" -or $_.Tags -like "Kat.D"} | Select-Object Device, Tags

I get these values via. Connect-PrtgServer. The output data for this is:

DEVICENAME {Kat.C}
DEVICENAME {Kat.B, C_OS_Win}
DEVICENAME {Kat.A, pingsensor}
DEVICENAME {Kat.A}
DEVICENAME {Kat.A, pingsensor} DEVICENAME {Kat.D}
DEVICENAME {Kat.D, pingsensor} DEVICENAME {Kat.B}

In this case, I want the other way around. I want to use -notlike to get all object, which doesn't have a "Kat.A, Kat.B, Kat.C or Kat.B" Tag. So I changed the code to:

Get-Sensor -Name Ping | where {$_.Tags -notlike "Kat.A" -or $_.Tags -notlike "Kat.B" -or $_.Tags -notlike "Kat.C" -or $_.Tags -notlike "Kat.D"} | Select-Object Device, Tags

But here is problem, that I get more values than needed:

DEVICENAME {Kat.D, test}
DEVICENAME {Kat.C, sql}
DEVICENAME {Kat.D, domaincontroller}
DEVICENAME {pingsensor, memberserver}
DEVICENAME {pingsensor, memberserver}
DEVICENAME {pingsensor, memberserver}
DEVICENAME {pingsensor, memberserver}
DEVICENAME {Kat.C}

I only need those four devices/tags which starts with pingsensor, because they don't contain a Kat.

Thank you for your upcoming help guys.

Best regards :)

Upvotes: 1

Views: 935

Answers (1)

mklement0
mklement0

Reputation: 437042

If this works:

$_.Tags -like "Kat.A" -or $_.Tags -like "Kat.B" -or $_.Tags -like "Kat.C" -or $_.Tags -like "Kat.D"`

and you want to negate it, enclose the expression in (...) and prepend -not:

-not ($_.Tags -like "Kat.A" -or $_.Tags -like "Kat.B" -or $_.Tags -like "Kat.C" -or $_.Tags -like "Kat.D")

However, since you're not using actual wildcard characters in your comparison patterns, there is no need for -like -just use -eq - or, preferably, given that $_.Tags seems to contain an array of values, use the -contains operator

-not ($_.Tags -contains "Kat.A" -or $_.Tags -contains "Kat.B" -or $_.Tags -contains "Kat.C" -or $_.Tags -contains "Kat.D")

Note, however, that if wildcard matching were truly needed, -contains wouldn't work.


Note:

  • Switching from ... -like ... -or ... to ... -notlike ... -and ... (analogously for -eq and -ne) is not the equivalent negation if the LHS operands are arrays, which seems to be the case for you.

  • The reason is that PowerShell's comparison operators act as filters with array-valued (list-like) LHS operands:

    • That is, with an array as the LHS, they return the sub-array of matching items rather than a Boolean ($true or $false).

    • Using an array as if it were a Boolean - which PowerShell lets you do implicitly, because it can quietly coerce any data type to a Boolean[1] - can have surprising results - see the example below.

# Define an array to serve as the LHS of a -like operation.
$tags = 'Kat.A', 'Kat.B'

# BOTH -like operations evaluate to $true in the implied Boolean context of `if`, 
# because they both return a non-empty subarray of matching elements.
if ($tags    -like 'Kat.A') { 'yes' }
if ($tags -notlike 'Kat.A') { 'yes' }

Somewhat counterintuitively, this outputs yes twice.

That is, the results of the array-filtering operations, when interpreted as Booleans, were both true, because in effect the two if conditionals were equivalent to:

  • [bool] @('Kat.A') -> $true
  • [bool] @('Kat.B') -> also $true

By contrast, the containment operators, -contains and its operands-reversed counterpart, -in, whose very purpose is to operate on arrays (list-like collections), always return a Boolean ($true or $false), so it is possible to properly negate
... -contains ... -or ... as ... -notcontains ... -and ...


[1] See the bottom section of this answer for a summary of the to-Boolean conversion rules.

Upvotes: 0

Related Questions