4

Recently I discovered some nasty behavior about assigning some version-like value to variable in Powershell (at least in 7.2.5).

At first I tried:

> $version = 1.2.3
> echo $version

I quickly found out that I have to escape thr value with quotes to make it string explicitly. This one works well:

> $version = "1.2.3"
> echo $version
1.2.3

The question is: Why does it work this way? Why does pwsh not throw some kind of error in the first example and just converts it to $null? Like in other cases:

> 12a
12a: The term '12a' is not recognized as a name of a cmdlet, function, script file, or executable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

> [int]12a
ParserError:
Line |
   1 |  [int]12a
     |       ~~~
     | Unexpected token '12a' in expression or statement.

So I believe literals like 1.2.3 are expected and managed by some rule. I tried to dig into documentation about expressions and assignments, but can't find the correct answer.

Palle Due
  • 5,929
  • 4
  • 17
  • 32

1 Answers1

6

why it works this way?

In effect, 1.2.3 is parsed as (1.2).3, i.e. as number literal 1.2 whose .3 property is retrieved.

Number literal 1.2 is an instance of a [double] (System.Double, a double-precision floating point value), and as such has no .3 property (or any other property).

By default, PowerShell quietly returns $null when you try to access a non-existent property on an object.

As Santiago Squarzon points out, you can turn such attempts into (statement-terminating) errors if you use Set-StrictMode -Version 2 or higher:

PS> Set-StrictMode -Version 2; 1.2.3
... The property '3' cannot be found on this object. ...

Note:

  • A given strict mode takes effect for the entire scope and any descendant scopes.

    • Note that this can make calling scripts or non-module functions fail, if they weren't designed with this strict mode in mind (it is common for code to assume that no strict mode is in effect).

    • To turn an active strict mode off, use Set-StrictMode -Off

  • Due to a long-standing bug - see GitHub issue #2798 and #16427 - the intrinsic members .Count and .Length - useful for unified handling of collections and scalars - are then not available.

  • The fact that strict mode version 2 or higher causes a statement-terminating error for attempts to access a non-existent property means that execution continues by default, with the next statement.

    • See this answer for how to make such errors script-terminating errors, i.e. fatal ones.
  • See iRon's comment below for links to GitHub issues discussing the perhaps surprising behavior at hand (1.2.3 quietly evaluating to $null), as well as a link to a suggestion to implement a PSScriptAnalyzer rule that would warn about expressions such as 1.2.3, so that you'd get a design-time warning when editing code in Visual Studio Code, for instance.


Finally, if you want to create 1.2.3 as a version object, you can cast from a string:

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • 1
    The issue itself is (also) described in: [`#15756` Unquoted numbers with two or more dots should cast to a **version** rather than `$Null`](https://github.com/PowerShell/PowerShell/issues/15756) and duplicate: [#15909 `$a = 1.2.3` gives no error message but doesn't do anything](https://github.com/PowerShell/PowerShell/issues/15909) and requested as PSScriptAnalyzer rule: [`#1698` Rule request: Warn when second dot on numeric literal causes member access expression](https://github.com/PowerShell/PSScriptAnalyzer/issues/1698) – iRon Jul 05 '22 at 07:08
  • 1
    Thanks, @iRon. I've updated the answer to point to your comment. – mklement0 Jul 05 '22 at 12:25