none
period in arguments RRS feed

  • Question

  • When working with java projects, I routinely bump into the behaviour where a -Dkey=val on the command line gets misinterpreted if it contains a period:

    #OK
    PS> echo -Dkey=value
    -Dkey=value

    #Unexpected. Cause of random toolchain failures.
    PS> echo -Dkey=value.v2
    -Dkey=value
    .v2

    #OK, workaround
    PS> echo "-Dkey=value.v2"
    -Dkey=value.v2

    Why does powershell split on the period... what is the general functionality that is causing this?

    The workaround is simply to quote the whole thing, but I'd like to understand the root cause.

    thanks, Mike.

    Thursday, April 11, 2013 11:50 PM

Answers

  • I was thinking the same thing, mario.exe. I think it has something to do with how PowerShell tokenizes parameters.


    Here's how PowerShell tokenizes 'echo -Dkey=value.v2':

    PS C:\> [system.management.automation.psparser]::tokenize('echo -Dkey=value.v2', [ref]$null) | ft -auto Content Type Start Length StartLine StartColumn EndLine EndColumn ------- ---- ----- ------ --------- ----------- ------- --------- echo Command 0 4 1 1 1 5 -Dkey=value CommandParameter 5 11 1 6 1 17 .v2 CommandArgument 16 3 1 17 1 20

    What I think happens is this:

    PowerShell gets a command called 'echo' and finds that it's an alias for 'Write-Output'.
    PowerShell tries to find a parameter called 'Dkey=value' for 'Write-Output', but it can't find it.
    PowerShell treats '-Dkey=value' and '.v2' as an array of psobjects because Write-Output takes an array of psobjects.
    PowerShell outputs the two values with Write-Output.

    A similar thing happens with 'Write-Host -Dkey=value.v2'

    The best reason I can find for why it splits on '.' is that according to the spec, '.' can't be used as a part of a command parameter.

    parameter-char: Any Unicode character except { } ( ) ; , | & . [ colon whitespace new-line-character


    It doesn't split on '=' because it's okay to have a command parameter like '-Dkey=value'.

    PS C:\> function myfunc([switch]${Dkey=value}) {"Dkey=value is ${Dkey=value}"} PS C:\> myfunc -Dkey=value:$true Dkey=value is True PS C:\> myfunc -Dkey=value:$false Dkey=value is False


    Now it's also possible to have a function with a parameter called 'Dkey=value.v2'...

    PS C:\> function myfunction(${Dkey=value.v2}) {"Dkey=value.v2 is ${Dkey=value.v2}"}


    ...and it works when the parameter is used positionally...

    PS C:\> myfunction hello Dkey=value.v2 is hello


    ...but not when it's used by name...

    PS C:\> myfunction -Dkey=value.v2 hello Dkey=value.v2 is .v2

    ...because the tokenizer splits the parameter name at the '.'.


    So, I guess my answer to why it splits at the '.' is because the spec says that the '.' can't be part of a command parameter. I don't know why that is like that. I don't know why it splits at the '.', but not at '=', or why it doesn't require a space between a parameter name and an argument that starts with a '.'. I can guess, but I've done enough of that with this post already. :-)

    Friday, April 12, 2013 6:22 AM

All replies

  • hmm...never noticed this but I played with this on PS 3.0 and it looks like if I remove the hyphen, the echo command puts Dkey=value.v2 on the next line. Maybe its the hyphen causing the issue?? What version of Powershell are you doing this on. 
    Friday, April 12, 2013 3:22 AM
  • I was thinking the same thing, mario.exe. I think it has something to do with how PowerShell tokenizes parameters.


    Here's how PowerShell tokenizes 'echo -Dkey=value.v2':

    PS C:\> [system.management.automation.psparser]::tokenize('echo -Dkey=value.v2', [ref]$null) | ft -auto Content Type Start Length StartLine StartColumn EndLine EndColumn ------- ---- ----- ------ --------- ----------- ------- --------- echo Command 0 4 1 1 1 5 -Dkey=value CommandParameter 5 11 1 6 1 17 .v2 CommandArgument 16 3 1 17 1 20

    What I think happens is this:

    PowerShell gets a command called 'echo' and finds that it's an alias for 'Write-Output'.
    PowerShell tries to find a parameter called 'Dkey=value' for 'Write-Output', but it can't find it.
    PowerShell treats '-Dkey=value' and '.v2' as an array of psobjects because Write-Output takes an array of psobjects.
    PowerShell outputs the two values with Write-Output.

    A similar thing happens with 'Write-Host -Dkey=value.v2'

    The best reason I can find for why it splits on '.' is that according to the spec, '.' can't be used as a part of a command parameter.

    parameter-char: Any Unicode character except { } ( ) ; , | & . [ colon whitespace new-line-character


    It doesn't split on '=' because it's okay to have a command parameter like '-Dkey=value'.

    PS C:\> function myfunc([switch]${Dkey=value}) {"Dkey=value is ${Dkey=value}"} PS C:\> myfunc -Dkey=value:$true Dkey=value is True PS C:\> myfunc -Dkey=value:$false Dkey=value is False


    Now it's also possible to have a function with a parameter called 'Dkey=value.v2'...

    PS C:\> function myfunction(${Dkey=value.v2}) {"Dkey=value.v2 is ${Dkey=value.v2}"}


    ...and it works when the parameter is used positionally...

    PS C:\> myfunction hello Dkey=value.v2 is hello


    ...but not when it's used by name...

    PS C:\> myfunction -Dkey=value.v2 hello Dkey=value.v2 is .v2

    ...because the tokenizer splits the parameter name at the '.'.


    So, I guess my answer to why it splits at the '.' is because the spec says that the '.' can't be part of a command parameter. I don't know why that is like that. I don't know why it splits at the '.', but not at '=', or why it doesn't require a space between a parameter name and an argument that starts with a '.'. I can guess, but I've done enough of that with this post already. :-)

    Friday, April 12, 2013 6:22 AM
  • Yes, notice if you escape the hyphen:

    C:> write-output -Dkey=value.v2 | % {$_.gettype();'>'+$_+'<'}
    IsPublic IsSerial Name                                     BaseType
    -------- -------- ----                                     --------
    True     True     String                                   System.Object
    >-Dkey=value<
    True     True     String                                   System.Object
    >.v2<
    
    C:> write-output Dkey=value.v2 | % {$_.gettype();'>'+$_+'<'}
    IsPublic IsSerial Name                                     BaseType
    -------- -------- ----                                     --------
    True     True     String                                   System.Object
    >Dkey=value.v2<
    
    C:> write-output `-Dkey=value.v2 | % {$_.gettype();'>'+$_+'<'}
    IsPublic IsSerial Name                                     BaseType
    -------- -------- ----                                     --------
    True     True     String                                   System.Object
    >-Dkey=value.v2<
    

    Friday, April 12, 2013 2:20 PM
  • I am using Powershell 2.

    PS> $Host.Version

    Major  Minor  Build  Revision
    -----  -----  -----  --------
    2      0      -1     -1


    Friday, April 12, 2013 5:45 PM