PowerShell: Strange Results in a simple script

Viewed 44

Can somebody please explain to me the odd results when I run this PowerShell script:

function Get-GoalQuantity {

    $loop = $true
    while ($loop) {
        Write-Host ""
        $quantity = Read-Host -Prompt "Enter Goal Quantity (1-15)"
        if ($quantity -match '^\d+$') {
            if (($quantity -gt 0) -and ($quantity -le 15)) {
                $loop = $false
                return $quantity
            }
        }
        Write-Host "Try again"
    }
}

While (1 -gt 0) {
    $testQty = Get-GoalQuantity
    Write-Host $testQty
}

It seems pretty straightforward, 0 and anything greater than 15 get rejected. As do any alphabetic entries. 1 and 10-15 are accepted correctly. But 2-9 are rejected, which makes no sense whatsoever. It's got something to do with it being a function. When coded as not a function it works correctly. I've checked that there are no spaces or other characters injected into the quantity value.

What PowerShell rule or oddity am I missing?

Any assistance would be appreciated.

1 Answers

Read-Host outputs string by default, you must convert it to int so the comparison is correct. This is how, in the future, you can approach the debugging of your code (break the logic into small pieces):

PS /> $quantity = Read-Host -Prompt "Enter Goal Quantity (1-15)"
Enter Goal Quantity (1-15): 2

PS /> $quantity -match '^\d+$' # => True
PS /> $quantity -gt 0          # => True
PS /> $quantity -le 15         # => False
PS /> ([int] $quantity) -le 15 # => True

In this case, you can use the -as type operator for safe type conversion of the string to int:

$quantity = (Read-Host -Prompt "Enter Goal Quantity (1-15)") -as [int]


As mklement0 points out in his helpful feedback, what I've proposed above is not enough to overcome certain inputs, in example '0xA' -as [int] would pass the validation.

To overcome this, and without overcomplicating the answer, it would be best if we validate the input with regex up front and then type convert to int, as mklement0 also propose in his helpful comment.

# get input
$quantity = Read-Host -Prompt "Enter Goal Quantity (1-15)"
# if regex validation passes
if($quantity -match '^\d+$') {
    # convert here
    $quantity = [int] $quantity

As an alternative, you can reverse the order of the comparison and let PowerShell perform the type conversion based on the LHS of the operation (this is explained briefly in Equality operators).

Here is an example for you to test what this means:

[string[]] (-20..20) | ForEach-Object {
    [pscustomobject]@{
        'Value'         = $_
        'LHS as int'    = 0 -lt $_ -and 15 -ge $_
        'LHS as string' = $_ -gt 0 -and $_ -le 15
    }
}
Related