Why is this do/until loop exiting with values starting 1?

Viewed 61

Built a menu system which for the most part works fine but I've hit a weird validation error and I'm scratching my head as to why this function is escaping when you answer with "11" (or indeed any number starting with 1)

function Get-MenuSelection {

    $totalOptions = 2

    do {
        $input = Read-Host "[1 <-> $totalOptions | (Q)uit - FIRST]"
        while (!$input) {
            $input = Read-Host "[1 <-> $totalOptions | (Q)uit - LOOP]"
        }

    } until ($input -lt $totalOptions -or $input -eq $totalOptions -or $input -eq "q")

Write-Host "exiting"
}

Get-MenuSelection

Output I'm getting:

./wtf.ps1
[1 <-> 2 | (Q)uit - FIRST]: 
[1 <-> 2 | (Q)uit - LOOP]: 
[1 <-> 2 | (Q)uit - LOOP]: test
[1 <-> 2 | (Q)uit - FIRST]: 22
[1 <-> 2 | (Q)uit - FIRST]: 9090
[1 <-> 2 | (Q)uit - FIRST]: 11
exiting

I'm clearly doing something wrong but just can't figure out what.

Solution

For those reading this some time in the future, I ended up with this - I chose to drop the 'q' options since it was just over-complicating the logic. Thanks to @AdminofThings and @mklement0 for the input. Appreciated.

function Get-MenuSelection {
    param (
        $output 
    )

    [int]$totalOptions = $output.Count
    do {
        try { [int]$answer = Read-Host "Options: [1 <-> $totalOptions]" }
        catch { }

        if ($answer -eq "0" -or $answer -gt $totalOptions) { 
            Write-Host "Invalid input detected. Ctrl+C to quit." 
        }
    } while ($answer -gt $totalOptions -or !$answer)

    $returnedAnswer = "menu_$answer"
    return $returnedAnswer
}
1 Answers

Since $input is an automatic/reserved variable, your code will not execute as intended. $input will likely result in an empty value during retrieval.

If we theoretically assume that $input is replaced by something that is not reserved, then a corresponding issue here is $input is a string and $totaloptions is an int. When PowerShell is faced with a comparison operation and both sides of the comparison don't match types, it will attempt to convert the righthand side (RHS) type to match the lefthand side (LHS). To get around this, you need to either cast $input as an [int] or bring $totaloptions to the LHS.

until ([int]$input -lt $totalOptions -or $input -eq $totalOptions -or $input -eq "q")
# OR
until ($totalOptions -gt $input -or $input -eq $totalOptions -or $input -eq "q")

An example of your situation:

#Unexpected Outcome
> [string]11 -lt [int]2
True

#Expected Outcome
> [int]11 -lt [int]2
False

#Expected Outcome
> [int]2 -gt [string]11
False
Related