Reputation: 2543
I came across weird code snippet in PowerShell while using array indexer. Why do you think it behave in following way?
Standard behavior I would expect is to get every time exact member of array, in this case first, zero based
(0, 1)[0] == 0 # as expected
(0, 1 -ne 2)[0] == 0 # as expected regardless second member would be casted from bool to int
(0, 1 -ne 0)[0] == 1 # magic starts here
So far I would expect it's something with casting result of 1 -ne 0
from bool to int and together with 0 on first place of array forms something as known exception but:
(0, 60 -ne 0)[0] == 60 # ignores first member and whole (-ne 0) part
(0, 60 -ne 1)[0] == 0 # as expected
(1, 60 -ne 0)[0] == 1 # as expected
At this point it seems that it takes into effect only if first member is 0
regardless its passed as variable and second part have to be exactly 1 -ne 0
# doesn't matter if first member is variable
$x = 0
($x, 1 -ne 0)[0] == 1 # same magic as before
($x, 0 -ne 1)[0] == 0 # as expected by may be caused by leading zero of 0 -ne 1
($x, 0 -ne 0)[0] == null # this is also very weird
($x, 1 -ne 1)[0] == 0 # maybe return of bool comparison or simply first member, I would expect null as before
I understand that all wondering about those things would be solved by simply preventing code from mixing bool and int in arrays. Way how I found that it's used in our legacy code used as ceilling
return ([int]$lastResultCode, 1 -ne 0)
instead of:
if ($lastResultCode == 0) return 1
else return $lastResultCode
But consider scenario when this code is already in air unable to change this code but also on that machine may be upgrades of PowerShell version and so may change of future behavior. For such a scenario I'd like please you for your opinions about what's driving this behavior.
Upvotes: 5
Views: 74
Reputation:
There are two things going on here:
The ,
operator has a higher precedence than comparison operators. So, expressions like:
(0, 1 -ne 0)[0]
are actually parsed as:
((0, 1) -ne 0)[0]
You can read more about PowerShell operator precedence under about_Operator_Precedence
.
PowerShell comparison operators can work with both scalars (single items) as well as arrays. When you use them with an array, they will return all items that meet the condition:
PS > (4, 5, 3, 1, 2) -gt 2 # All items greater than 2
4
5
3
PS > (4, 5, 3, 1, 2) -ne 3 # All items that do not equal 3
4
5
1
2
PS >
With these points in mind, the behavior you are seeing can easily be explained. The expression:
(0, 1 -ne 0)[0]
is first parsed as:
((0, 1) -ne 0)[0]
which then becomes:
(1)[0]
because -ne
returns the items from (0, 1)
that do not equal 0
, which is just 1
.
The solution is simply to use an extra set of parenthesis:
(0, (1 -ne 0))[0]
This will cause PowerShell to evaluate the 1 -ne 0
part first and behave as you expect:
PS > (0, (1 -ne 0))
0
True
PS > (0, (1 -ne 0))[0]
0
PS >
Upvotes: 6