How to navigate nested loops within Powershell - powershell

I am trying to do something of the following nature
if(...){
}
elseif(...){
if(...){
}
else{
...
}
}
else{
...
}
However, it seems that powershell does not like having both an if and and else statement within the elseif loop. Any workarounds for this? Thanks for your help! I am really new to Powershell
I have tried switch statements but they do not make sense for what I am trying to do

Not an answer per se, but I tested the structure exactly as provided and it works just fine:
function iftest($a, $b, $c) {
if ($a) {
'$a true. $b and $c not tested.'
}
elseif ($b) {
if ($c) {
'$a false. $b true. $c true.'
}
else {
'$a false. $b true. $c false.'
}
}
else {
'$a false. $b false. $c not tested.'
}
}
# command # output
iftest $true $false $false # $a true. $b and $c not tested.
iftest $false $false $false # $a false. $b false. $c not tested.
iftest $false $true $false # $a false. $b true. $c true.
iftest $false $true $true # $a false. $b true. $c false.
As noted by mclayton in the comments, formatting goes a long way toward making the structure clear.

Related

Powershell Write-Output strange behavior

I have a script below that behaves really strangely.
For the first case I try to return an empty list specifying -NoEnumarate flag. But for some reason, the function returns $null
For the second case I do precisely the same as for the first case with the only difference: rather than providing -InputObject as a positional parameter, I provide it explicitly.
Why in the first case I get $null and in the second case I get the expected result - empty List[string]. I'm really confused.
# case 1
function Foo {
$result = [System.Collections.Generic.List[string]]::new()
Write-Output -InputObject $result -NoEnumerate
}
$foo = Foo
if ($foo -is [System.Collections.Generic.List[string]]) {
Write-Host 'Foo'
}
if ($null -eq $foo) {
Write-Host '$foo is null. Whyyy?'
}
# case 2
function Bar {
$result = [System.Collections.Generic.List[string]]::new()
Write-Output $result -NoEnumerate
}
$bar = Bar
if ($bar -is [System.Collections.Generic.List[string]]) {
Write-Host '$bar is list[string]'
}

Sorting not working in a Powershell function [duplicate]

This question already has answers here:
How do I pass multiple parameters into a function in PowerShell?
(15 answers)
Powershell function call changing passed string into int
(1 answer)
Closed 1 year ago.
I have the following function in Powershell. It always returns False and also the sorting of the arrays doesn't work within the function while it works in the console. The function is
# $a = [121, 144, 19, 161, 19, 144, 19, 11]
# $b = [121, 14641, 20736, 361, 25921, 361, 20736, 361]
function comp($a, $b) {
if( -Not ($a.length -Eq $b.length) || -Not $a || -Not $b) {
return $false
}
$a = $a | sort
$b = $b | sort
# Adding echo statements here to check if $a and $b are sorted tells that both are NOT sorted despite assigning them to the same variable
# Assigning the sorted arrays to other variables such as $x and $y again doesn't solve the problem. $x and $y also have the unsorted values
for($i=0;$i -lt $a.length;$i++) {
if( -Not ($b[$i] -Eq $a[$i]*$a[$i])) {
return $false
}
}
return $true
}
Note: $a and $b in the top are initialized without [ and ] and it is just provided to give emphasis that they are arrays.
The above function returns False while it must be True. I then tried this
function comp($a, $b) {
if( -Not ($a.length -Eq $b.length) || -Not $a || -Not $b) {
return $false
}
for($i=0;$i -lt $a.length;$i++) {
$flag = $false
for($j=0;$j -lt $b.length; $j++) {
if($b[$j] -Eq $a[$i]*$a[$i]) {
$flag = $true
# Never gets into this i.e. never executed
break;
}
}
if( -Not $flag) {
return $flag
}
}
return $true
}
But this works on the console when run without a function. Please see the image below
It didn't return False. And hence, the output is True which is correct
Now see the output for the above functions
Now for the second one
What is wrong here?
You're passing the arguments to comp incorrectly.
PowerShell's command invocation syntax expects you to pass arguments separated by whitespace, not a comma-separated list.
Change:
comp($a, $b)
to:
comp $a $b
# or
comp -a $a -b $b
See the about_Command_Syntax help topic for more information on how to invoke commands in PowerShell

Powershell 'x or y' assignment

There are several languages that provide either a defaulting or logical or mechanism for assignment:
a = b || c;
a = b or c
a="${b:-$c}"
a = b ? b : c;
So far the only equivalent I've found in Powershell Core is the exceedingly verbose:
$a = if ($b) { $b } else { $c }
which in some cases has to become
$a = if ($b -ne $null) { $b } else { $c }
Is there a better alternative [edit:] which doesn't sacrifice readability?
There's no || short-circuit operator in PowerShell assignments, and nothing equivalent to Perl's // "defined-or" operator - but you can construct a simple null coalesce imitation like so:
function ?? {
param(
[Parameter(Mandatory=$true,ValueFromRemainingArguments=$true,Position=0)]
[psobject[]]$InputObject,
[switch]$Truthy
)
foreach($object in $InputObject){
if($Truthy -and $object){
return $object
}
elseif($object -ne $null){
return $object
}
}
}
Then use like:
$a = ?? $b $c
or, if you want to just have return anything that would have evaluated to $true like in your first example:
$a = ?? $b $c -Truthy
So a simple method for reducing this:
$a = if ($b -ne $null) { $b } else { $c }
Would be to use the fact that true and false are just one or zero and could be used as an array index like so:
$a = #($c, $b)[$b -ne $null]

How to set PowerShell variables to True or False

PowerShell logic question:
$a = 1
$b = 2
if ($a = $b) {
$ans = $true
}
else {
$ans = $false
}
Write-Host $ans
Output:
True
Can someone explain to me why this evaluates as true? Is it because I assign $ans to true first? Also, could someone show me how to get this to evaluate the way I think it should?
You're doing an assignment $a = $b, the assignment succeeds and that returns true because b was true, so the first case will always evaluate to true at the moment, instead use a comparison: $a -eq $b.
More detailed information on comparisons in powershell.

convert "Yes" or "No" to boolean

I want to parse user values contained in .CSV file. I don't want my users to enter "Yes" or "No" but instead enter "True" or "False". In each case I want to convert to the equivalent boolean values: $true or $false. Ideally I would like a default value, so if there's misspelt "Yes or "No" I would return my default value: $true or $false.
Hence, I wondered if there is a neat way of doing this other than
if(){} else (){}
One way is a switch statement:
$bool = switch ($string) {
'yes' { $true }
'no' { $false }
}
Add a clause default if you want to handle values that are neither "yes" nor "no":
$bool = switch ($string) {
'yes' { $true }
'no' { $false }
default { 'neither yes nor no' }
}
Another option might be a simple comparison:
$string -eq 'yes' # matches just "yes"
or
$string -match '^y(es)?$' # matches "y" or "yes"
These expressions would evaluate to $true if the string is matched, otherwise to $false.
Ah, the magic of powershell functions, and invoke expression.
function Yes { $true }
function No { $false }
$magicBool = & $answer
Note: This is case insensitive, but will not handle misspellings
If the only possible values are "Yes" and "No" then probably the simplest way is
$result = $value -eq 'Yes'
With misspelled values and the default $false the above will do as well.
With misspelled values and the default $true this will work
$result = $value -ne 'No'
All of these are valid approaches. If you are looking for a one liner, this will validate it is an acceptable value and set to boolean true if in the 'true' value set. This will also give you a default $false value.
$result = #("true","false","yes","no") -contains $value -and #("true","yes") -contains $value
For a default $true value you would need something like so.
$result = $true
if (#("true","false","yes","no") -contains $value) {
$result = #("true","yes") -contains $value
}
Without a full snippet of your existing code, something like this would probably be an alternative path to take, as opposed to a string of IF statements.
NOTE: This will not handle simple 'Y' or 'N' input, but is case insensitive. So, you should be able to see 'yes' or 'YES' working, as well.
$myVar = Read-Host 'What is your answer?'
switch ($myVar)
{
Yes {$myVarConverted = $true; break}
True {$myVarConverted = $true; break}
No {$myVarConverted = $false; break}
False {$myVarConverted = $false; break}
default {"Invalid Input"; break}
}
Write-Host $myVarConverted
Please see my additional comment on your question about the 'misspelling' caveat. That's difficult to code around without specific restrictions or requirements.
Here's the way I do Yes-No answers:
function ask-user
{
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[string] $question
)
Process
{ $answer = read-Host $question
if ("yes" -match $answer) {$true}
elseif ("no" -match $answer) {$false}
else {ask-user $question}
}
}
You can easily substitute true and false for yes and no.
This one is case insensitive, and will match valid abbreviations. (Y or N).
In the case of misspellings, it asks again. Yeah, I could have done it without recursion, but I'm lazy.
These are great solutions above, but let me just say that this whole topic just proves the vast shortcomings of Powershell...
[System.Convert]::ToBoolean("False") -eq $true ?
[System.Convert]::ToBoolean("0") -eq $true ?
Really?
Give me a f--kin break.
For me :-
Function convert2Bool($this) { return ($("False","0","","N","No",'$False',"Off") -notcontains [string]$this) }
can adjust if you don't want $null blank-string going to $false, else fine.