Take the 2-minute tour ×
Server Fault is a question and answer site for professional system and network administrators. It's 100% free, no registration required.

I recently wrote this answer and stumbled on something interesting.

get-aduser -filter {-not (description -eq "auto")} | measure-object

and

get-aduser -filter {description -ne "auto"} | measure-object

return two very different things when run against the same data, with the first command returning the expected value. It appears at first glance that users with a null value in the description field are not returned in as matches in the second command, even though NULL clearly doesn't equal "auto".

A couple of people in chat have looked at this and verified that I'm not insane. What's going on here?

share|improve this question
1  
Interesting that piping it through Where-object and applying the filter that doesn't work above works. get-aduser -filter * -Properties description| ?{$_.description -ne "Auto"} | measure –  Mike Jan 10 '13 at 17:39
    
@Mike Yes, this appears to be behavior of the -ne comparison operator within the -Filter block only. Specifically, when the input value of the comparison is $null. –  jscott Jan 10 '13 at 17:41
1  
Like and notlike seem to work similarly. {description -notlike "something"} does not work, but {-not (description -like "something")} does. Also the one that works takes considerably longer to evaluate. Like the broken one isn't evaluating all the objects it should. –  Mike Jan 10 '13 at 17:45
    
@Mike That's right. I actually stumbled on this using -notlike originally, but switched to -ne after I realized I wasn't getting what I wanted. TBH, I forgot I had even tried that until you mentioned it - but I can reproduce it as well. –  MDMarra Jan 10 '13 at 17:47
2  
Just a thought but maybe PoSH's -eq/-ne clause tries to behave like a SQL =/<>? In SQL, foo = NULL and foo <> NULL will always return false, because NULL is 'incomparable' -- only the foo IS NULL and foo IS NOT NULL special operators will work. The behaviour must be similar in PoSH, where your -not (foo -eq "bar") filter would return everything for which (foo -eq "bar") returned $false, which foo -eq $null would do. Instead, how about if (!foo -or foo -ne "bar") (equivalent SQL foo IS NULL OR foo <> 'bar')? –  jimbobmcgee Jan 14 '13 at 17:40
show 5 more comments

1 Answer

up vote 4 down vote accepted

The key difference between the two is the first command does not involve a direct comparison of values to get all the results, and the second command does. The first command includes NULL results and the second one does not (as MDMarra already discovered). Both commands start with this cmdlet:

get-aduser

When going through the below, remember that the results of this cmdlet include all AD users regardless of anything else in the -filter parameter after it.

Now let's break down the two parts that are different. The first one:

{-not (description -eq "auto")}

...means

  1. "figure out where the description attribute equals the text string "auto". For this comparison to work, a string needs to exist in the description field for the -eq operator to be able to compare it to "auto". NULL values are dropped from this comparison since it can't compare a NULL to a string value.
  2. independently of the -eq filter parameter give me EVERYTHING that is NOT the result of (description -eq "auto"), which will include NULLs, because the original cmdlet, get-aduser, includes all AD users. It did not have to compare anything to anything else with the -not operator. It just gave you everything besides what the results of the (description -eq "auto") filter were.

In your example suppose you have 1 AD user that has their description equal to "auto", a few hundred with something other than "auto" and a few hundred with NULL descriptions. Stepping through the command logic it will do:

  1. Give me all AD users (get-aduser) where the description equals "auto" - results in 1 user
  2. Give me all the AD users that are NOT what you just gave me - result is the few hundred with something else AND the few hundred that have NULL.

Since it didn't have to compare anything to anything else using the -not operator, the result included the NULL description users that were captured in the original get-aduser cmdlet.

The second command:

{description -ne "auto"}

...means

  1. "figure out where the description attribute does not equal the exact string "auto". Again, for this comparison to work, a string needs to exist in the description field for the -ne operator to be able to compare it to "auto". NULL values are dropped from this comparison since it can't compare a NULL to a string value.

In your example again suppose you have 1 AD user that has their description equal to "auto", a few hundred with something other than "auto" and a few hundred with NULL descriptions. Stepping through the command logic it will do:

  1. Give me all AD users where the description is not equal to "auto" - results in the few hundred users with the something other than "auto" in their description. It doesn't pull the users with NULL descriptions because it can't compare a NULL to a text string.

Either way the whole difference between the two commands is definitely non-intuitive.

Using this command you should be able to catch the NULLs with an "-and" in there too like this:

{description -ne "auto" -and description -ne $NULL}

I'm not 100% on the syntax since I can't test it right now, and there is probably a better way to do it than this too. When it's all broken down it is fairly anti-climatic and took a lot of typing to explain, but I've run into weird stuff like this before using the various operators and a lot of trial and error since I can never remember all the caveats that go along with using each one.

Reference: http://technet.microsoft.com/en-us/library/hh847732.aspx:

Comparison Operators

Use comparison operators (-eq, -ne, -gt, -lt, -le, -ge) to compare values and test conditions. For example, you can compare two string values to determine whether they are equal.

The comparison operators include the match operators (-match, -notmatch), which find patterns by using regular expressions; the replace operator (-replace), which uses regular expressions to change input values; the like operators (-like, -notlike), which find patterns using wildcard characters (*); and the containment operators (in, -notin, -contains, -notcontains), which determine whether a test value appears in a reference set.

They also include the bitwise operators (-bAND, -bOR, -bXOR, -bNOT) to manipulate the bit patterns in values.

For more information, see about_Comparison_Operators

Logical Operators

Use logical operators (-and, -or, -xor, -not, !) to connect conditional statements into a single complex conditional. For example, you can use a logical -and operator to create an object filter with two different conditions.

For more information, see about_Logical_Operators.

share|improve this answer
    
Good overview for sure, but why are null values excluded from the -ne and -notlike operators? That's the real head scratcher. I'm wondering if its by design an has some esoteric .net spec explanation or if its a bug or unexpected behavior? –  MDMarra Jan 14 '13 at 21:56
    
Wait, just read more closely. It looks like they only compare strings and null attributes are actually null and not an empty string. Interesting if unintuitive. –  MDMarra Jan 14 '13 at 21:58
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.