You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Parameter values disambiguated with their explicit parameter names should not require quoting #4576
The two Get-Content commands should be equivalent:
In the 1st case, the quoting implies that -t.txt is a parameter value rather than a name.
In the 2nd case, the explicit use of parameter -LiteralPath, which requires a value, should unambiguously identify the next token as the parameter value - even if it starts with - - whether quoted or not.
Actual behavior
hi
Get-Content : Parameter cannot be processed because the parameter name 't' is ambiguous. Possible matches include: -TotalCount -Tail.
...
That is, despite being preceded by -LiteralPath, which requires a parameter value, unquoted-t.txt was not interpreted as that parameter's value, but as another parameter name.
Environment data
PowerShell Core v6.0.0-beta.5 on macOS 10.12.6
PowerShell Core v6.0.0-beta.5 on Ubuntu 16.04.3 LTS
PowerShell Core v6.0.0-beta.5 on Microsoft Windows 10 Pro (64-bit; v10.0.15063)
Windows PowerShell v5.1.15063.483 on Microsoft Windows 10 Pro (64-bit; v10.0.15063)
With your proposal in place, looking at that command should it be interpreted as
Get-Command-Module '-Name'-Name 'Get-Help'
or
Get-Command-Name 'Get-Help'-Module # Missing an argument for parameter 'Module'...
This could cause unexpected/unpredictable behavior and lead to a bad UX. This also opens a Pandora's box of exceptions like passing string parameters with subexpression operators. Strings that contain special tokens should be quoted while strings without special token can be passed without quoting as a kind of convenient syntactic sugar. Unquoted string parameters should be the exception, not the rule, IMO.
that there's no syntactical ambiguity: in PowerShell, a parameter either requires a value or is a switch; so, for any given parameter name specified explicitly, it is unambiguously implied whether the next token is a parameter value or a different parameter.
that even single-character - suffixes - where there's no obvious resemblance to a parameter name - can trigger the problem; e.g., -n.
that the current behavior is inconsistent:
>Write-Output-InputObject -z
-z
>Write-Output-InputObject -n
Write-Output : Missing an argument for parameter 'InputObject'. Specify a parameter of type 'System.Management.Automation.PSObject[]' and try again.
As you can see, -z, because it happens not to be the start of a parameter name, is accepted as -InputObject's parameter value.
By contrast, because -n happens to be the start of -NoEnumerate, it is interpreted as that, and then -InputObject has no value, and the command breaks.
I suppose that one way to resolve the problem would be to disallow unquoted values that start with -, but my concern would then be PowerShell's CLI, where introducing such an additional quoting requirement (e.g., the need to pass a script filename -t.ps1explicitly quoted) would further aggravate the quoting headaches, e.g., when calling from bash:
# Currently works.
powershell -noprofile -file -t.ps1
# Here's what the need to quote explicitly would entail:# *2* layers of quoting: an outer layer interpreted by Bash that gets removed before# PS is passed the value, and the inner layer for PowerShell's benefit.
powershell -noprofile -file "'-t.ps1'"
I guess the CLI could continue to operate differently, but such inconsistency is ill-advised.
that there's no syntactical ambiguity: in PowerShell, a parameter either requires a value or is a switch;
Yes and I'm suggesting this would introduce that ambiguity. In my example you could consider Get-Help the positional parameter for -Name and -Name as the string parameter for -Module. That's not what I as a user would expect. I would expect to be told I forgot to provide a value for -Module. Instead, I would just get nothing in return assuming I don't have a Get-Help command in a -Name module.
I suppose that one way to resolve the problem would be to disallow unquoted values that start with -
If any change is made, I would say that this one is the way to go. I didn't even know - could begin any unquoted string value.
As for the rest, I'm not sure that's the case unless i misunderstand you. It appears you are seeking consistency between parent shells and PowerShell syntax. Trying to make the PowerShell CLI consistent with other Shells is a bit futile.
When PowerShell is the parent shell, you already have to quote -t.ps1
PS C:\> powershell -noprofile -file -t.ps1
Processing -File '-t' failed because the file does not have a '.ps1' extension. Specify a valid Windows PowerShell script file name, and then try again.
PS C:\> powershell -noprofile -file '-t.ps1'
# Executes the contents of -t.ps1
I wouldn't expect PowerShell's argument parsing to be consistent with Bash, CMD, ksh, python REPL, or other interactive CLI.
Yes and I'm suggesting this would introduce that ambiguity.
There's no ambiguity, because what I'm proposing is state-dependent, based on the specific syntax of the command in question:
With a clean slate, -Name is unambiguously the parameter name.
Once that has been recognized, given that the -Name parameter requires a value, the next token is unambiguously the parameter value - even if it looks like a parameter.
It's not that common a pattern in POSIX-like shells, but the equivalent is an option with a mandatory option-argument; for instance, if you want to pass a script filename to sed via its -f option, the next token is invariably treated as the filename, even if it starts with -:
sed -f -my_script ... # -my-script is invariably treated as the option *argument*
Again, in the case of Get-Command -Module -Name Get-Help it is obvious what the intent is, but let's not forget that relative to the intent it is a syntax error - based on a misunderstanding of how the -Module parameter works.
In other cases, such as in my -n example, the intent may be quite different, and since -InputObject -n is a syntactically unambiguous way to pass -n as a value - because -InputObjectrequires one - it should be treated as such.
PS C:\> powershell -noprofile -file -t.ps1
Processing -File '-t' failed because the file does not have a '.ps1' extension. Specify a valid Windows PowerShell script file name, and then try again.
That this fails from inside PowerShell looks like a bug.
I wouldn't expect PowerShell's argument parsing to be consistent with Bash, CMD, ksh, python REPL, or other interactive CLI.
I didn't mean other shells, I meant the consistency between PowerShell's CLI and parameter passing to cmdlets and functions:
If:
powershell -noprofile -file -t.ps1 works (which it should and currently does from cmd.exe and bash and, as stated, that it doesn't from inside PowerShell should be considered a bug),
something like Get-Content -LiteralPath -t.ps1 should work too.
that it doesn't from inside PowerShell should be considered a bug
I don't think so. It should be considered a difference of syntax between shells. How a shell parses arguments to pass to binary is up to that shell and there are only similarities between shells. PowerShell as a shell will have a different method for parsing and passing arguments to binaries then Bash, CMD, etc. that would be expected. What one shell deems an important token another may deem irrelevant.
Internally, PowerShell is consistent:
#Doesn't work
powershell -noprofile -file -t.ps1
Get-Content-LiteralPath -t.ps1
# Works
powershell -noprofile -file '-t.ps1'Get-Content-LiteralPath '-t.ps1'
The way in which it fails tells us that something is amiss - the two commands fail for different reasons:
Get-Content -LiteralPath -t.ps1 fails, because -t.ps1 is interpreted as a parameter name (the starting point of our discussion - purely syntactically, given that --prefixed barewords are allowed, this should work).
powershell -noprofile -file -t.ps1 fails due to an unrelated bug, because, even though -t.ps1 is interpreted as a parameter value in principle, it unexpectedly considers the .ps1 suffix a separate argument, and -file only sees filename -t, not -t.ps1
The latter, as I've just discovered, is a generic parsing bug:
(At least I see no reason why, if --prefixed tokens can be barewords in principle, a .-prefixed suffix should cause the bareword to be split into 2 arguments.)
# OK>Write-Output-InputObject -t
-t
# BROKEN>Write-Output-InputObject -t.ps1
Write-Output : A positional parameter cannot be found that accepts argument '.ps1'.
Essentially, PS parses Write-Output -InputObject -t.ps1 as if .ps1 had been passed as a separate argument - Write-Output -InputObject -t .ps1 - even though I see no good reason for that - Write-Output -InputObject t.ps1 (value doesn't start with -) works just fine, for instance.
Huh.. to add to the weirdness... this is totally possible...
$Variable='temp'Write-Output-t*+_*^%$variable#@!<>/?\]=+-Write-Output-InputObject -t*+_*^%$variable#@!<>/?\]=+-#output: for both:-t*+_*^%$variable#@!<>/?\]=+-
(,),',", [,{,} and . all break it, but any of the others, including what would normally be a variable, is treated as a literal string.
I really think the solution is to treat - as a reserved prefix for named parameters. I would expect
Write-Output-NonexistentParameter
to throw a NamedParameterNotFound exception, and I would expect
Write-Output-InputObject -NonexistentParameter
To throw a MissingArgument exception. I definitely would not expect either to output -NonexistentParameter as a string.
I've created #4578 to focus on the aspect of --prefixed bareword parsing (and to generally ask the question whether it should be supported).
I'm with you with respect to Write-Output -NonexistentParameter: without a preceding, valid parameter name that requires a value, -NonexistentParameter should be parsed as a parameter name (and in order to force interpretation as a positional value the user would have to quoting; e.g., '-NonexistentParam' or `-NonexistentParam)
I disagree with respect to Write-Output -InputObject -NonexistentParameter (as stated, the use of -InputObject unambiguously makes the following token its value, so -NonexistentParam should print, as happens now, but inconsistently), but #4578 will hopefully bring clarity.
Feel free to continue discussing, but I'm closing the issue because I can't see adding more complexity to the parameter binder to change this behavior.
The "workaround" is in my opinion the preferred style - use quotes.
There is no confusion to human readers.
There is no potential ambiguity for the parameter binder to deal with.
The script is future proof, adding a new parameter that might match some argument won't cause a problem.
as stated, the use of -InputObject unambiguously makes the following token its value, so -NonexistentParam should print, as happens now, but inconsistently
This may be unambiguous from a code standpoint. But from a user standpoint, that would be really confusing.
Now my very important -wwhatif file is deleted. :( Maybe I was just trying to test out the command but flubbed the -whatif parameter. My expectation as a user would be that I should be told -Path is missing its value or that -wwhatif is not a valid paramter, not that -wwhatif is either deleted or not found.
That's a good example, and I can see how requiring quoting ultimately makes for a user-friendlier solution.
Jason Shirk (@lzybkr) seems to agree with you, so this means that the unquoted---prefixed-bareword-as-value loophole should be closed: #4591
For the sake of a sane OUTSIDE CLI, however, I hope that something like powershell -noprofile -file -t.ps1 will continue to work from outside of PowerShell.
Actually, on further reflection, I do not think that something like
Remove-Item-force -Path -wwhatif
is a good reason to abstain from using the definition of -Path determine the syntactic role of -wwhatif. An actual filename matching a misspelled -WhatIf is too obscure a scenario to drive the decision on how to properly parse such commands.
Based on the subsequent discussion in #4591, I'm now more than ever convinced that the only predictable way to handle --prefixed barewords is as I've originally proposed, given the desire to retain the ability to use such barewords, in the context of ValueFromRemainingArguments.
Activity
markekraus commentedon Aug 15, 2017
I'll weight in that this is not a good idea, IMO.
For example:
With your proposal in place, looking at that command should it be interpreted as
or
This could cause unexpected/unpredictable behavior and lead to a bad UX. This also opens a Pandora's box of exceptions like passing string parameters with subexpression operators. Strings that contain special tokens should be quoted while strings without special token can be passed without quoting as a kind of convenient syntactic sugar. Unquoted string parameters should be the exception, not the rule, IMO.
mklement0 commentedon Aug 15, 2017
Mark Kraus (@markekraus):
I get your concern, but do note:
that there's no syntactical ambiguity: in PowerShell, a parameter either requires a value or is a switch; so, for any given parameter name specified explicitly, it is unambiguously implied whether the next token is a parameter value or a different parameter.
that even single-character
-suffixes - where there's no obvious resemblance to a parameter name - can trigger the problem; e.g.,-n.that the current behavior is inconsistent:
As you can see,
-z, because it happens not to be the start of a parameter name, is accepted as-InputObject's parameter value.By contrast, because
-nhappens to be the start of-NoEnumerate, it is interpreted as that, and then-InputObjecthas no value, and the command breaks.I suppose that one way to resolve the problem would be to disallow unquoted values that start with
-, but my concern would then be PowerShell's CLI, where introducing such an additional quoting requirement (e.g., the need to pass a script filename-t.ps1explicitly quoted) would further aggravate the quoting headaches, e.g., when calling frombash:I guess the CLI could continue to operate differently, but such inconsistency is ill-advised.
markekraus commentedon Aug 15, 2017
Yes and I'm suggesting this would introduce that ambiguity. In my example you could consider
Get-Helpthe positional parameter for-Nameand-Nameas the string parameter for-Module. That's not what I as a user would expect. I would expect to be told I forgot to provide a value for-Module. Instead, I would just get nothing in return assuming I don't have aGet-Helpcommand in a-Namemodule.If any change is made, I would say that this one is the way to go. I didn't even know
-could begin any unquoted string value.As for the rest, I'm not sure that's the case unless i misunderstand you. It appears you are seeking consistency between parent shells and PowerShell syntax. Trying to make the PowerShell CLI consistent with other Shells is a bit futile.
When PowerShell is the parent shell, you already have to quote
-t.ps1I wouldn't expect PowerShell's argument parsing to be consistent with Bash, CMD, ksh, python REPL, or other interactive CLI.
mklement0 commentedon Aug 15, 2017
There's no ambiguity, because what I'm proposing is state-dependent, based on the specific syntax of the command in question:
-Nameis unambiguously the parameter name.-Nameparameter requires a value, the next token is unambiguously the parameter value - even if it looks like a parameter.It's not that common a pattern in POSIX-like shells, but the equivalent is an option with a mandatory option-argument; for instance, if you want to pass a script filename to
sedvia its-foption, the next token is invariably treated as the filename, even if it starts with-:sed -f -my_script ... # -my-script is invariably treated as the option *argument*Again, in the case of
Get-Command -Module -Name Get-Helpit is obvious what the intent is, but let's not forget that relative to the intent it is a syntax error - based on a misunderstanding of how the-Moduleparameter works.In other cases, such as in my
-nexample, the intent may be quite different, and since-InputObject -nis a syntactically unambiguous way to pass-nas a value - because-InputObjectrequires one - it should be treated as such.That this fails from inside PowerShell looks like a bug.
I didn't mean other shells, I meant the consistency between PowerShell's CLI and parameter passing to cmdlets and functions:
If:
powershell -noprofile -file -t.ps1works (which it should and currently does fromcmd.exeandbashand, as stated, that it doesn't from inside PowerShell should be considered a bug),something like
Get-Content -LiteralPath -t.ps1should work too.markekraus commentedon Aug 15, 2017
I don't think so. It should be considered a difference of syntax between shells. How a shell parses arguments to pass to binary is up to that shell and there are only similarities between shells. PowerShell as a shell will have a different method for parsing and passing arguments to binaries then Bash, CMD, etc. that would be expected. What one shell deems an important token another may deem irrelevant.
Internally, PowerShell is consistent:
mklement0 commentedon Aug 15, 2017
The way in which it fails tells us that something is amiss - the two commands fail for different reasons:
Get-Content -LiteralPath -t.ps1fails, because-t.ps1is interpreted as a parameter name (the starting point of our discussion - purely syntactically, given that--prefixed barewords are allowed, this should work).powershell -noprofile -file -t.ps1fails due to an unrelated bug, because, even though-t.ps1is interpreted as a parameter value in principle, it unexpectedly considers the.ps1suffix a separate argument, and-fileonly sees filename-t, not-t.ps1The latter, as I've just discovered, is a generic parsing bug:
(At least I see no reason why, if
--prefixed tokens can be barewords in principle, a.-prefixed suffix should cause the bareword to be split into 2 arguments.)Essentially, PS parses
Write-Output -InputObject -t.ps1as if.ps1had been passed as a separate argument -Write-Output -InputObject -t .ps1- even though I see no good reason for that -Write-Output -InputObject t.ps1(value doesn't start with-) works just fine, for instance.markekraus commentedon Aug 15, 2017
Huh.. to add to the weirdness... this is totally possible...
(,),',",[,{,}and.all break it, but any of the others, including what would normally be a variable, is treated as a literal string.I really think the solution is to treat
-as a reserved prefix for named parameters. I would expectto throw a
NamedParameterNotFoundexception, and I would expectTo throw a
MissingArgumentexception. I definitely would not expect either to output-NonexistentParameteras a string.mklement0 commentedon Aug 15, 2017
Good find re lack of expansion.
I've created #4578 to focus on the aspect of
--prefixed bareword parsing (and to generally ask the question whether it should be supported).I'm with you with respect to
Write-Output -NonexistentParameter: without a preceding, valid parameter name that requires a value,-NonexistentParametershould be parsed as a parameter name (and in order to force interpretation as a positional value the user would have to quoting; e.g.,'-NonexistentParam'or`-NonexistentParam)I disagree with respect to
Write-Output -InputObject -NonexistentParameter(as stated, the use of-InputObjectunambiguously makes the following token its value, so-NonexistentParamshould print, as happens now, but inconsistently), but #4578 will hopefully bring clarity.lzybkr commentedon Aug 15, 2017
Feel free to continue discussing, but I'm closing the issue because I can't see adding more complexity to the parameter binder to change this behavior.
The "workaround" is in my opinion the preferred style - use quotes.
There is no confusion to human readers.
There is no potential ambiguity for the parameter binder to deal with.
The script is future proof, adding a new parameter that might match some argument won't cause a problem.
markekraus commentedon Aug 15, 2017
Michael Klement (@mklement0)
This may be unambiguous from a code standpoint. But from a user standpoint, that would be really confusing.
Now my very important
-wwhatiffile is deleted. :( Maybe I was just trying to test out the command but flubbed the-whatifparameter. My expectation as a user would be that I should be told-Pathis missing its value or that-wwhatifis not a valid paramter, not that-wwhatifis either deleted or not found.mklement0 commentedon Aug 16, 2017
Mark Kraus (@markekraus):
That's a good example, and I can see how requiring quoting ultimately makes for a user-friendlier solution.
Jason Shirk (@lzybkr) seems to agree with you, so this means that the unquoted-
--prefixed-bareword-as-value loophole should be closed: #4591For the sake of a sane OUTSIDE CLI, however, I hope that something like
powershell -noprofile -file -t.ps1will continue to work from outside of PowerShell.PetSerAl commentedon Aug 16, 2017
Another workaround would be to use parameter with explicit argument syntax:
mklement0 commentedon Aug 20, 2017
Mark Kraus (@markekraus):
Actually, on further reflection, I do not think that something like
is a good reason to abstain from using the definition of
-Pathdetermine the syntactic role of-wwhatif. An actual filename matching a misspelled-WhatIfis too obscure a scenario to drive the decision on how to properly parse such commands.Based on the subsequent discussion in #4591, I'm now more than ever convinced that the only predictable way to handle
--prefixed barewords is as I've originally proposed, given the desire to retain the ability to use such barewords, in the context ofValueFromRemainingArguments.Please see #4624