2

I am having trouble coming up with a way to do complex logic using the where-object in PowerShell

I have the following code, but separating the and from the ORs is not working as intended.

Get-ADComputer -Filter * | Where-Object {
    ($_.Enabled) -and
    (($_.DistinguishedName -contains 'world') -or
    ($_.DistinguishedName -contains 'foo')) -and
    (($_.SID -contains 'bar') -or
    ($_.SID -contains 'something else'))
}

If I do this in c# I get results, but in powershell I do not.

Any thoughts on how to get around this?

TIA

4
  • 1
    what happens if you add one test at a time? at what point does the test fail to give the expected results? Commented Mar 19, 2021 at 15:51
  • Why would the SID contain strings? Commented Mar 19, 2021 at 15:55
  • 2
    also, the -contains operator is a collection operator, not a string operator. you likely otta use either -match or -like with wildcards. Commented Mar 19, 2021 at 15:55
  • Unusually, -and and -or have equal precedence in powershell. Usually "and" has higher precedence. Commented Nov 6 at 14:39

3 Answers 3

4

The main issue is very likely with -contains, this is the operator you would use to check whether an item exists in an array, for example:

'apple', 'banana', 'pineapple' -contains 'apple'

# True

This operator does not work in partial matches, only exact ones. For partial matches you could use -like or, the recommended one in this case would be the -match operator with a regex OR pattern:

Get-ADComputer -Filter 'enabled -eq $true' | Where-Object {
    $_.DistinguishedName -match 'world|foo' -and
    $_.SID -match 'bar|something else'
}

Using -like the condition becomes very similar as what you currently have, however you do need the * wildcards (for the partial match), no parentheses are needed:

Get-ADComputer -Filter 'enabled -eq $true' | Where-Object {
    $_.DistinguishedName -like '*world*' -or $_.DistinguishedName -like '*foo*' -and
    $_.SID -like '*bar*' -or $_.SID -like '*something else*'
}

If Where-Object isn't performing well (it can be slow), you might consider a more classic approach using foreach and if conditions:

$result = foreach ($computer in Get-ADComputer -Filter 'enabled -eq $true') {
    if ($computer.DistinguishedName -match 'World|Foo' -and $computer.SID -match 'bar|something else') {
        $computer
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you, this is exactly the knowledge sharing I needed. I appreciate every ones input and sorry for my lack of knowledge.
2

this is not an Answer - so please let me know when you have read it so that i can delete it.

this is your code with more informative indentation, with the needless extra parens removed, and with spaces around the operators.

Get-ADComputer -Filter * |
    Where-Object {
        $_.Enabled -and
        ($_.DistinguishedName -Contains "world" -or
            $_.DistinguishedName -Contains "foo") -and
        ($_.SID -Contains "bar" -or
            $_.SID -Contains "something else")
        }

note that the -contains operator is NOT for strings ... it is for membership in a collection. if you want to test against strings, use -match, or .Contains(), or -like with wildcards.

6 Comments

"the -contains operator is NOT for strings" - kinda sounds like an answer Lee ;-)
@MathiasR.Jessen - ok ... i will remove that part of the Answer. thank you for the feedback! [grin]
Why not just remove the disclaimer? Why do you hate answering questions so much? :)
@MathiasR.Jessen - many times the Answer feels more like a comment to me. i seem to have a bit of a problem deciding what side of the line this one otta be on.
Read, Thank you very much.
|
0

Powershell doesn't give "and" a higher precedence than "or", like in every other language. So instead of putting parentheses everywhere and hoping for the best, just put them around the "and" parts so they evaluate first. Note that -contains only matches whole words in an array. .contains() would work, but it's case sensitive. -match is probably the best alternative. The first "and" evaluates first anyway (left to right), but it's nice to have some symmetry. (I can't make it say powershell instead of bash?)

$objects = 'enabled,distinguishedname,sid
true,world,1
"",world2,1
true,foo,bar
true,foo,something else' | convertfrom-csv 

$objects | Where-Object {
    ($_.Enabled -and $_.DistinguishedName -contains 'world') -or
    ($_.DistinguishedName -contains 'foo' -and $_.SID -contains 'bar') -or
    $_.SID -contains 'something else'
}

Output

enabled distinguishedname sid
------- ----------------- ---
true    world             1
true    foo               bar
true    foo               something else

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.