How to logical OR multiple variables in Powershell [duplicate] - powershell

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]

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

PowerShell return multiple values from if condition

I have a Powershell script returning data from an API which works fine as long as I only attempt to return one $device.realm, but I need multiple realms. I'm a newb to PS.
Any help here is really appreciated
Here is my code
$Output = forEach ($device in $devices) {
if ($device.realmName -eq 'Archive') {
[PSCustomObject]#{
HostName = $device.name
IPAddress = $device.primaryInterfaceAddress
Realm = $device.realmName
SerialNumbers = (($device.dynamicFields | where { $_.name -EQ "serial number" } | Select-Object -ExpandProperty values) -join "," | out-string).TrimEnd()
}| Select-Object Hostname,IPAddress,Realm,SerialNumbers | Export-csv C:\temp\Archive.csv -notype -Append
}
I need to return multiple $device.realms as in
if ($device.realmName -eq 'Archive' -and 'Default' -and 'Farms')
Once I add the additional -and's every realm is returned instead of just the one's I need to return.
I believe the issue at hand here is that the statement within the If block that you're querying as ($device.realmName -eq 'Archive' -and 'Default' -and 'Farms')
is not, when evaluated logically "Evaluate true if the device realmname is Archive, Default, or Farms." It is evaluating whether device.realmname is archive, and then just interpreting the two -ands in your example as true, as they are not querying a comparison, but just the presence of a non-null string. Not sure what is leading it to return everything, I'd have to see some more direct examples to be sure, but in my experience that is most common when you include an -or in a comparison pointing to a nonnull string, which will make the entire statement true.
What I would suggest is as follows: Use the regex operators built in to powershell for cases like this. You could use
if($device.realmname -eq 'Archive' -or $Device.realmname -eq 'farm' -or $device.realmname -eq 'Default')
which would, I believe, return what you are looking for, but I find it a bit complex. More complicated queries on a single property, I find, are easiest to do via -match, through something invoking the -match operator, which allows you to build a regex query statement that can include Or's or And's with a bit simpler of a synatax, like so:
if($Device.realmName -match 'Archive|Farm|Default')

Why doesn't my condition (-not A -and (A -or B -or C)) work?

I wrote _in function to detect if we must install packages or not. The arguments -packages and +packages work but +base and +full don't work, how can I fix it ?
$scriptArgs=$args
function _in {
Param($find)
foreach ($i in $scriptArgs) {
if ($i -eq $find) {
return 1
}
}
return 0
}
# Install packages
if (-not (_in("-packages")) -and (_in("+packages") -or _in("+base") -or _in("+full"))) {
PrintInfo "* Installing packages"
}
This works:
PS> powershell .\scripts\win\install_zds.ps1 +packages
* Installing packages
PS> powershell .\scripts\win\install_zds.ps1 +packages -packages
-packages disables package installation and +packages enables package installation.
This doesn't work:
PS> powershell .\scripts\win\install_zds.ps1 +base
PS> powershell .\scripts\win\install_zds.ps1 +full
+base and +full should enable package installation.
EDIT: I would like understand why:
I follow PetSerAI comment, then, I remove the parentheses like this:
if (-not (_in "-packages") -and ((_in "+packages") -or (_in "+base") -or (_in "+full"))) { }
This works, but I don't understand why. I found this explain about parentheses in PowerShell:
Powershell is a parsed and interpreted language. The interpreter see's parenthesis as a control structure and is not expected or required at the Call Site.
But with test-function("Hello"), hello is string not a structure.
function Test-Function {
Param(
[string]
$hello
)
"String: $hello"
}
Test-Function("Hello")
Test-Function "Hello"
The expression
-not (_in("-packages")) -and (_in("+packages") -or _in("+base") -or _in("+full"))
isn't evaluated in the way you apparently expect.
PowerShell functions (unlike method calls) expect their arguments as a whitespace separated list without parentheses, i.e. _in("foo") should be _in "foo". The parentheses aren't syntactically wrong (_in("foo") is a valid expression), but PowerShell will parse the parentheses as a grouping expression, which is evaluated first. Meaning that PowerShell will first expand _in("foo") to _in "foo" before actually calling the function.
However, since you're putting function calls in a boolean expression you need to put grouping parentheses around each function call to have the function calls evaluated first, so that the result of the function calls is used in the boolean expression:
(_in "foo") -and (_in "bar")
Without that the boolean operators would be parsed as parameters for the first function. In other words
_in("foo") -and _in("bar")
would be expanded to
_in "foo" -and _in "bar"
which would then invoke the function _in() with the arguments foo, -and, _in, and bar.
Because of that your condition must be written as
-not (_in "-packages") -and ((_in "+packages") -or (_in "+base") -or (_in "+full"))
With that said, what you're trying to implement would not only re-implement the -in/-contains operators, it is also contrary to normal PowerShell parameter handling. I strongly recommend you look into advanced function parameters and parameter sets. They work on both function and script level.
Example:
[CmdletBinding(DefaultParameterSetName='none')]
Param(
[Parameter(ParameterSetName='base', Mandatory=$true)]
[Switch]$Base,
[Parameter(ParameterSetName='full', Mandatory=$true)]
[Switch]$Full
)
switch ($PSCmdlet.ParameterSetName) {
'none' { 'install nothing' }
'base' { 'base install' }
'full' { 'full install' }
}
Note that Powershell is very unusual when it comes to -and and -or, they have equal precedence. Most other languages aren't like this (C#, vbscript...). It seems like it was overlooked in the beginning, and now they don't want to break existing scripts.
$true -or $true -and $false
False
$true -or ($true -and $false)
True
This is more typical behavior, with + and *. * has higher priority than +.
1 + 2 * 3
7
(1 + 2) * 3
9

PowerShell IF statement Variables

im curious how to use the IF statement logic further in my code. Let me elaborate with an example.
$a=1
$b=10000
if(($a=1) -or ($b=1))
{write-host ""} #Here, I want to write what ever variable was $true
#in the if statement above.... so I want it to
#write "1" which was $a and was $true in the if
#statement...
I could write more logic to accomplish this, but im wondering if the values that the if statement used can be used again in the code. Im thinking there is a "hidden" variable maybe?
($a=1) is usually an assignment statement. This is not the case in Powershell, but it sure looks like a bug. The Powershell way to do comparison is to use -eq. So,
if(($a -eq 1) -or ($b -eq1))
Now, the simple solution is a bit different. What happens, if both $a and $b happen to be 1?
$comp = 1 # Value to compare against
$a=1
$b=100
if($a -eq $comp) { write-host "`$a: $a" }
if($b -eq $comp) { write-host "`$b: $b" }
This approach is easy to understand, which is in most of the cases more important than other factors like speed.

Conditional logic no longer working as expected?

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