powershell logical operators and $Error variable - powershell

Recently I had to check whether some errors occurred when my script is executed. First I've tried to check whether $Error is $null. The strange thing for me was that I haven't got any result from (neither True, nor False). Then i've wrote:
if (($error -eq $null) -or ($error -ne $null)) {Write-Host "NULL"}
And nothing was in the output. This made me very confused. I've found that such thing happens for all variables which are of System.Collections.ArrayList type.
Maybe someone knows the explanation why this happens, because for me this looks like a bug?
Version of Powershell, on which I found this, is 3.0.

#mjolinor's answer tries to explain it, but is incomplete.
When you do (1,2,3) -eq 1, you get back 1. In this case what -eq does with an array is to return the element that is equal to the RHS, and nothing if no match occurs.
On the other hand, if you do 1 -eq (1,2,3), you get False, because the above occurs only when the array is the LHS. So it is not true that the -eq operator always does behaves like the above case when it comes to arrays.
Now, coming on to the -ne usage. When you do (1,2,3) -ne 1, you get the array 2,3. That is, it returns the elements that are not equal to the RHS. And similar to -eq, 1 -ne (1,2,3), will return True
Coming to your condition - ($error -eq $null) -or ($error -ne $null)
When $error is empty, $error -eq $null will return nothing ( and is hence False in a bool statement). This is of course because there is no element matching $null in $error. Also, $error -ne $null will also return nothing ( and hence is False in a bool statement) because $error is empty and there is no element in it that is not $null.
Hence, when $error is empty, your statement will be false and the block inside if will not be executed.
If $error were not empty, either of the condition would have been true, and you would have seen the write-hostexecuted.
So how do you really solve this problem?
The straightforward way is to check the length of the $error array:
if($error.length -gt 0){
write-host "error occured"
}
Also, read this article that talks about various error handling strategies - http://blogs.technet.com/b/heyscriptingguy/archive/2011/05/12/powershell-error-handling-and-why-you-should-care.aspx

When the -eq operator is used against an array (or arraylist), it returns all the members of the array that satisfiy the condition.
($error -eq $null) says "I want all the members of the $error arraylist that are nulls." It can't return anything but $null.
When you use it in an IF, the result is going to be cast as [bool]. $Null evaluates to $false when cast as [bool].
($error -eq $null) can never be True.
$x = new-object collections.arraylist
[void]$x.Add('a')
[void]$x.add('b')
($x -eq $null).count
[bool]($x -eq $null)
[void]$x.Add($Null)
($x -eq $null).count
[bool]($x -eq $null)
0
False
1
False

Related

PowerShell Variable IF Statement from windows form

I'm trying to so write a PowerShell script that performs a task based on the input from a windows form.
If ($ComboBoxOffice.Text -eq 'Norwich', $CheckBoxNoEquipment.Checked -eq 'False' )
I can get it to work with just the following code:
If ($ComboBoxOffice.Text -eq 'Norwich')
Any ideas on how would go about only actioning the IF statement based on the input from the first code?
Assuming you only want to proceed when BOTH conditions are satisfied, use the -and operator:
if($ComboBoxOffice.Text -eq 'Norwich' -and $CheckBoxNoEquipment.Checked -eq $false){
# ...
}
If you want to proceed when either condition is satisfied, use -or:
if($ComboBoxOffice.Text -eq 'Norwich' -or $CheckBoxNoEquipment.Checked -eq $false){
# ...
}
Notice that I'm using the special boolean variable $false rather than the string 'False', to avoid type confusion bugs

How to check if a collection has a null reference

I just discovered that when you apply bool operators on a collection, it acts as a filter on that collection.
So the following will return all elements that are not null:
$objectArray = #('a','b','c','d')
$objectArray -ne $null
But how can I check if the collection's reference is null?
Trevor Sullivan's if () test forces the $objectArray to cast to a boolean.
[bool]$null #is $false
[bool]#(1,2,3) # is $true , so it looks good.
But empty arrays mislead it:
[bool]#() # is $false , so it's not an accurate test.
I suggest $null -eq $objectArray:
NB. It really opens the question of why you want to know if it's $null, specifically. Trevor's answer is typical and good enough for any common use.
NB. My answer includes an uncommon, but useful suggestion - when you have a literal value for one side of a comparison, put it on the left if you can.
0 -lt $counter
$null -eq $thing
"text" -eq $variable
4 -in $collection
It's less common, so looks less familiar, but it's more resilient against PowerShell implicit casting doing something you don't expect.
All you have to do is test the variable for $true or $false. If it's $false, then it's a null reference, otherwise the opposite is true.
if (!$objectArray) {
}
The following tells you if the reference is null:
[Object]::ReferenceEquals($objectArray, $null)
Testing if the variable is $true or $false does not always work because an empty collection will cast to false:
$objectArray = #()
if (!$objectArray) {
'The array is not actually null'
}

How to do a -Contain -Not : String contains "Anything other than"

How can I script "Does String -contains -not _" / "Does the string contain anything other than _"?
I'm not stuck as I've found a good enough work around. More curiosity than anything else.
Example:
$String = 1,1,1,2,5
$String -contains !(1)
This always comes up False
My solution at the moments is to remove the 1's and see if it's null like so:
$String2 = $String -ne 1
if ([String]::IsNullOrEmpty($String2)) {
Write-Host "True"
} else {
Write-Host "False"
}
Real World Example:
My script is designed to try a certain action until it works. In this case get-msoluser.
At the end of my script I want to count any errors (and list them later) but there will always be an error listed for "get-msoluser" as it fails until it works. So I'm trying to not include that certain error in the count.
$Errors = $Error.InvocationInfo.MyCommand.Name
if ($Errors -contains !("get-msoluser")) {
Write-Host "There was an error I actually care about"
}
INSTEAD I have to do this:
$Errors = $Error.InvocationInfo.MyCommand.Name
$ErrorsICareAbout = $Errors -ne "get-msoluser"
if ([String]::IsNullOrEmpty($ErrorsICareAbout)) {
Write-Host "$ErrorsICareAbout.Count"
} else {
Write-Host "There were errors you actually cared about"
}
Am I missing something that's right under my nose?
You simply need to use -notcontains or add the not operator around then entire -contains comparison like this:
If ($Errors -notcontains ("get-msoluser"))
or
If (!($Errors -contains ("get-msoluser")))
Rather than filtering out the error, try not producing an error in the first place. To suppress errors from a particular command, you can set the error action to SilentlyContinue.
Write-Error 'fail' -ErrorAction SilentlyContinue
So in the case of retrying until Get-MsOlUser works, you could use something like
while($msolUser -eq $null) {
$msolUser = Get-MsOlUser ... -ErrorAction SilentlyContinue
#Wait a second before retrying.
Start-Sleep -Seconds 1
}
#Now work with $msolUser
(You probably also want to put an upper limit on the number of retries)

Powershell function argument default: Weirdness when it has a type constraint

If I have a function parameter WITHOUT the type constraint:
> function a ($s=$null) {if ($s -eq $null) {Write-Host "HI"} if ($s -eq "") {Write-Host "KK"}}
> a
HI
Now if I add the type constraint to it, the $null is interpreted differently:
> function a ([string]$s=$null) {if ($s -eq $null) {Write-Host "HI"} if ($s -eq "") {Write-Host "KK"}}
> a
KK
I can't find doc that explain this. It's also not consistent.
In your first example (function a), $s is equivalent to $null - it's truly null.
In your second example (function b), because you're casting $s to a [string] object, it's actually an empty String (equivalent to [String]::Empty), not $null.
You can check this by adding the following to each of your functions:
if($s -eq [String]::Empty){"empty!"};
Only b will print empty! - a will evaluate this to $false
Alternately, add this:
$s|get-member
a will actually throw an error - the same error you'll get if you run $null|get-member. b will show you that $s is a string and list all of the members of that class.

The PowerShell -and conditional operator

Either I do not understand the documentation on MSDN or the documentation is incorrect.
if($user_sam -ne "" -and $user_case -ne "")
{
Write-Host "Waaay! Both vars have values!"
}
else
{
Write-Host "One or both of the vars are empty!"
}
I hope you understand what I am attempting to output. I want to populate $user_sam and $user_case in order to access the first statement!
You can simplify it to
if ($user_sam -and $user_case) {
...
}
because empty strings coerce to $false (and so does $null, for that matter).
Another option:
if( ![string]::IsNullOrEmpty($user_sam) -and ![string]::IsNullOrEmpty($user_case) )
{
...
}
Try like this:
if($user_sam -ne $NULL -and $user_case -ne $NULL)
Empty variables are $null and then different from "" ([string]::empty).
The code that you have shown will do what you want iff those properties equal "" when they are not filled in. If they equal $null when not filled in for example, then they will not equal "". Here is an example to prove the point that what you have will work for "":
$foo = 1
$bar = 1
$foo -eq 1 -and $bar -eq 1
True
$foo -eq 1 -and $bar -eq 2
False