4
$UserChoice = Read-Host "Enter # of tool you want to run"
switch -exact ($UserChoice) {
    1 {Write-Host 'You selected 1'}
    1a {Write-Host 'You selected 1a'}
    1b {Write-Host 'You selected 1b'}
    1c {Write-Host 'You selected 1c'}
    1d {Write-Host 'You selected 1d'}
}

Preface: I know the fix is to put the comparison values in quotes, ex: '1d'. I want to know WHY PowerShell is acting the way it is for learning sake.

  1. If I enter 1b at prompt, it returns 1b whereas if I enter 1d it returns nothing, why?
  2. If I enter 1b at prompt, it returns 1b whereas if I enter 1 it returns 1 and 1d, why?
  3. It seems PowerShell is interpreting d in a switch statement comparison value different for some reason, why?

--EDIT: I just noticed VISUALLY the comparison values are different colors (1 & 1d are darker) so I guess PowerShell ISE syntax highlighting is telling us they're being treated differently. 4. What do the colors mean (maroon/dark-RedOrPurple VS purple? enter image description here

mklement0
  • 382,024
  • 64
  • 607
  • 775
gregg
  • 1,084
  • 1
  • 12
  • 25

2 Answers2

6

Before explaining why the 1d label is "special", I should note that the -exact mode (which is the default mode of comparison for a switch statement) is probably a bit misleading.

It simply means "use the -eq operator to compare input values to case labels".

The reason 1d behaves differently is that PowerShell doesn't recognize the expression 1d as a string. Instead, it interprets d is a numerical suffix signifying the [decimal] type, and the case label value is thus the same as if you'd written 1.0 or $([decimal]1).

The result is that comparison to the input string "1" comes out the same for both - "1" -eq 1 and "1" -eq 1d are both true, thanks to PowerShell's overloaded operators.

If you ever expand your options further, you'll encounter the same problem with 1l (l = [long]), and, if using PowerShell 7, eventually 1n, 1s, 1u, and 1y.

Quote the switch labels to avoid PowerShell parsing them as a numerical expressions:

$UserChoice = Read-Host "Enter # of tool you want to run"
switch -exact ($UserChoice) {
    '1'  {Write-Host 'You selected 1'}
    '1a' {Write-Host 'You selected 1a'}
    '1b' {Write-Host 'You selected 1b'}
    '1c' {Write-Host 'You selected 1c'}
    '1d' {Write-Host 'You selected 1d'}
}

See the about_Numeric_Literals help topic for a more comprehensive overview of suffixes interpreted as numerical modfiers by PowerShell.

Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206
  • Thanks so I guess `d` is telling PowerShell to change the datatype thus changing its value so won't match, got it. I updated my question with a picture showing the colors being different, can you explain what the colors mean (question 4)? Guessing PowerShell ISE syntax highlighting is trying to tell me ones a string vs decimal. Question 5)why doesn't the $UserChoice variable datatype change when you enter 1d (aka why is switch comparison value affected & an undefined variable datatype isn't)? – gregg Mar 30 '22 at 19:56
  • @gregg That's exactly right! PowerShell's parser first tries to see if the case label is a constant, variable or pipeline expression (like `1d`, or `$true`, or `$('1' + 'c')` for example), but if it encounters a bare word that _isn't_ one of those (for example `1b` or `hello` or `gregg`) it then falls back to interpreting it as a string literal, the same as if you'd written `"1b"` instead of just `1b`. The color differences you see in ISE shows _exactly that_ - unlike the other three labels, `1d` is colored the same as any other numerical constant. – Mathias R. Jessen Mar 30 '22 at 20:01
  • These cmdlets help see it as well: `(1).GetType()`; `(1d).GetType()`; `(1b).GetType()`. So while `switch` is simple the powershell logic/parsing complicates it. Since you work at MS thought I'd point out their guide examples ALSO don't put quotes on most switch labels: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_switch?view=powershell-7.2 https://learn.microsoft.com/en-us/powershell/scripting/learn/deep-dives/everything-about-switch?view=powershell-7.2 – gregg Mar 30 '22 at 21:58
  • @gregg I don't work at MS, but the documentation is open for public revision and feedback, simply click through the links at the bottom of the page (where it says "Submit and view feedback for ...") if you think the examples could be better :) – Mathias R. Jessen Mar 30 '22 at 22:03
1

To add to Mathias' excellent answer:

  • The switch statement's branch conditionals should never have allowed the use of barewords (unquoted tokens serving as strings).

    • It amounts to an inappropriate blending of PowerShell's two fundamental parsing modes, argument mode and expression mode.

      • In expression mode - which is the appropriate one to apply - strings must be quoted; e.g., to match a string with verbatim value 1d, it must be represented as '1d', which disambiguates it from the number literal 1d, a [decimal].
    • See GitHub issue #3668 for a discussion.

  • The -exact switch opts into the default behavior and, given the non-exact nature of this behavior, is confusingly named.

mklement0
  • 382,024
  • 64
  • 607
  • 775