Conditional logic no longer working as expected? - powershell

Its been a long day and I think I'm going mad. I wanted to test for a file and generate an email if none existed. Here it is pared down to its most minimal:
> IF('False' -eq (Test-Path D:\Scripts\SFTP\Import\*)){ECHO "SEND EMAIL"}
> SEND EMAIL
__________________________________________________________________________
> IF((Test-Path D:\Scripts\SFTP\Import\*) -eq 'False'){ECHO "SEND EMAIL"}
>
Why doesn't the second command work?
I've tried running the Test-Path outside of the 'IF' statement into a variable and then testing against that, again it doesn't work.
If I simply run the 'Test-Path' command I get a boolean 'False' as expected. I've used conditional logic in this way before and its worked.
What am I doing wrong?

The reason is this. In the first one you have a string as the first operand of the comparison. This forces PS to coerce the second operand to a string if possible. In this case that means calling the .ToString() method of the boolean which would return the 'False' string (if the boolean is actually false of course). In the second case though, you are presenting a boolean as the first operand, so the string is being coerced to a boolean. Obviously it is not working. To avoid these issues, use the builtin $false (or $true) variable. Personally I would suggest just negating the Test-Path. Here are a couple of ways that should work:
if( -NOT (Test-Path D:\Scripts\SFTP\Import\*)){
if( (Test-Path D:\Scripts\SFTP\Import\*) -eq $false){

For the coercing rules of powershell
'False' -eq (Test-Path D:\Scripts\SFTP\Import\*)
the second value of comparision is evaluated as [string]
here
(Test-Path D:\Scripts\SFTP\Import\*) -eq 'False'
the second value of comparison can't be evaluated as [Bool] then it fails.
For bool comparin is optima use the automatic variable $false and $true

Related

Powershell Understanding "continue"

So in the spirit of this short but sweet tutorial I'm trying to filter out disabled user and only work on the "Enabled" users with this code. (FYI Search-ADAccount needs elevated)
$EXPusers = (Search-ADAccount -AccountExpired -UsersOnly)
foreach($user in $EXPusers){
$UENB = $user.Enabled
$UENB # Sanity Check
if($UENB -eq "False"){
continue
}
# All of this is functioning
# disable user
# Logoff user
# Send email
}
In my lab $EXPusers just resolves to one user that is disabled or Enabled=False. So what happens is no matter what I set $UENB equal to it keeps sending mail. Seems to me that if it's "False" it should skip that iteration and not process the rest of the foreach statement and move to the next user, in this case do nothing.
What am I missing?
The reason why it's failing is because you're comparing a boolean (the Enabled Property of an ADAccount instance is a bool) with a string. It's important to note that, in PowerShell a string that is not empty will always be $true and, since in your comparison the string is in the right hand side (RHS) of the comparison, PowerShell attempts type coercion as the same type of the left hand side (LHS), so the string 'false' is converted to a boolean during the comparison which results being $true.
In about Comparison Operators documentation on the Equality operators section we can read the following:
The equality operator can compare objects of different types. It is important to understand that the value is on the right-hand side of the comparison can be converted to the type of the left-hand side value for comparison.
A simple demo:
[bool] 'false' # => $true
$false -eq 'false' # => $false
'false' -eq $false # => $true
The last comparison results in $true because the boolean $false in the RHS is converted to string and, in PowerShell, [string] $false results in the literal string false.
In conclusion, by simply changing your if condition your code would work properly:
if($false -eq $UENB) {
continue
}
The other alternative would be to use the logical -not operator:
if(-not $UENB) {
continue
}

How to logical OR multiple variables in Powershell [duplicate]

This question already has answers here:
PowerShell's parsing modes: argument (command) mode vs. expression mode
(2 answers)
Closed 1 year ago.
I thought this was simple. And I'm sure it is. But, I can't seem to crack it.
I have 3 functions that return a true or false value.
In a later if evaluation I am trying to logical or the 3 results together.
Something like this:
if (Fnc1 -or Fnc2 -or Fnc3) { write-host "Yes" }
Not only is Powershell highlighting the syntax differently for the first Fnc1 from the others, it's only returning true or false based on the value of Fnc1 from what I can tell.
I know this works:
if ((Fnc1 -eq $true) -or (Fnc2 -eq $true) -or (Fnc3 -eq $true)) { write-host "Yes" }
But, that seems like overkill and un-necessary.
What am I missing?
PowerShell attempts to parse the -or token as a function parameter when you place it after a function name like that. Surround each function call in ():
if ((Fnc1) -or (Fnc2) -or (Fnc3)) { write-host "Yes" }
another way to get that is to use the -contains collection operator. lookee ...
function Get-True {$True}
function Get-False {$False}
#((Get-True), (Get-False), (Get-True)) -contains $True
output = True
if all those were Get-False, the result would be False.
note that this method requires that all the function calls be run before the -contains operator can test anything. that means the -or solution would be more efficient since that would run each function call in sequence and stop when the -or was satisfied.
the -or solution can be much more efficient if the function calls take any significant amount of time or resources.
thanks to #SagePourpre for pointing that out. [grin]

Powershell - Test-Path - If and return don't work

i am new to Powershell, the following if statement is executed even when the return of Test-Path is true.
$CheckFile = Test-Path $output_folder\$item
if ($CheckFile = "False"){
Does something
}
Honestly i don't know why.
For further Information, just ask.
Thanks in advance
You are using a string value of "False" which, when interpreted as a boolean will always be true, because it's a string that has a value. Beyond the string being empty or not, the value is not used in that conversion.
You're also using the assignment operator = not the equality operator -eq. Use $False:
$CheckFile = Test-Path $output_folder\$item
if ($CheckFile -eq $False){
Does something
}

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'
}

Beginner Problems with Equals Operator

everyone trying to learn Powershell off and on and I'm stuck on this problem. I cannot seem to find an equals operator that this code will accept at the = true portion .
Ive tried -eq, =, ==, and === .
Trying to get the Msg box to pop up if this Test-path command returns a true condition.
$wshell = New-Object -ComObject Wscript.Shell
If( Test-Path 'C:\wmw\~$test.xlsx' **= True)**
{
$wshell.Popup("Hey $Env:ComputerName This file is in use!",0,"test")}
else
{$wshell.Popup("Hey $Env:ComputerName This file is not in use!",0,"test")}
First of all, the literal for true is $true in PowerShell. And the operator for equality comparison is -eq. Then there is the issue that parameters to cmdlets start with - and you'd need to wrap the command in parentheses. Otherwise -eq would be interpreted as a (non-existent) parameter to Test-Path. So putting that all together:
If( (Test-Path 'C:\wmw\~$test.xlsx') -eq $True) { ... }
or, since if just needs a value that can be coerced to a boolean you don't even need the explicit comparison in most cases:
if (Test-Path 'C:\wmw\~$test.xlsx') { ... }
One hint for future exploration of the shell: Read the error messages. Most of the time they are helpful.
Omitting the parentheses and using -eq tells you about the fact that it's interpreted as a parameter:
Test-Path : A parameter cannot be found that matches parameter name 'eq'.
Same with = which is interpreted as a parameter value here:
Test-Path : A positional parameter cannot be found that accepts argument '='.
Using parentheses correctly and using -eq breaks the parser, admittedly:
You must provide a value expression following the '-eq' operator.
Unexpected token 'True' in expression or statement.
Missing closing ')' after expression in 'if' statement.
Unexpected token ')' in expression or statement.
Using parentheses and = is helpful again:
The assignment expression is not valid. The input to an assignment operator must be an object that is able to accept assignments, such as a variable or a property.