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
Argument-mode parsing: Why is a subexpression at the start of an unquoted token treated differently than a variable reference? #3217
changed the title [-]Argument-mode parsing: Why is a subexpression at the start treated differently than a variable reference?[/-][+]Argument-mode parsing: Why is a subexpression at the start of an unquoted token treated differently than a variable reference?[/+]on Feb 27, 2017
So, by the time the arguments get to the parameter binder it's already either broken up (or not) the tokens. There's definitely a difference between a variable reference $a and a subexpression $(2+2) so i would expect a difference in the behavior between write-output $(2+2)/H and write-output $a/H.
I suspect that it's the location of the obvious string element which causes the behavioral change which changes it from two arguments to one. I haven't tracked that down though. write-output H/... is an obvious string (since that's how it starts), pretty much anything that follows it which isn't a token separator (space for example) is just going to be tacked on the end.
As for write-output .$HOME - remember that the . serves two functions, one as a invocation operator and another as a property reference operator, depending on context. In this case, we've definitely got 2 tokens . (the invocation operator) and $HOME, so we emit two lines.
FWIW, Write-Host sees these two tokens as well:
Write-Host .$HOME
. /Users/james
note the space. Why the difference? The difference between write-host and write-object is the parameter type for the input object. in write-host the type is object and in write-output the parameter type is PSObject[].
To provide more motivation for my question, which is primarily about the user experience, not the technical underpinnings:
It is obscure to have behavior depend on the order of tokens in this case, especially since variable references and subexpressions are not treated differently when explicitly embedded inside "..."
Write-Output "$(2 + 2)/H" yields only 1 argument.
In POSIX-like shells, a command/arithmetic substitution (the equivalent of a subexpression in PS) does work as the first token: echo $(( 2 + 2 ))/H yields 1 argument, 4/H
That said, (the expanded results of) unquoted tokens in POSIX-like shells are subject to shell expansions that are often unwanted, leading to the annoyance that double quotes must be used everywhere (and beginners forgetting them is a constant source of bugs).
PowerShell generally frees us from that annoyance, so it's unfortunate that it doesn't do so in this case (because it results in multiple arguments).
Generally, remembering when it works and when it doesn't, in the absence of an intuitive reason, is a challenge and, I suspect, will be an ongoing source of confusion.
The same obscure behavior applies to juxtaposing differently quoted substrings (which are commonly used in POSIX-like shells):
Write-Output bare"I'm quoted" -> 1 argument, because the 1st token is unquoted: bareI'm quoted
Write-Output "I'm quoted"bare -> 2 arguments, because the 1st token is quoted: I'm quoted and bare
As for the .$HOME case: Given that the token is a non-initial token parsed in argument mode, it shouldn't be interpreted as the invocation operator.
If unquoted tokens are to be treated like expandable strings (i.e., as if the token were explicitly enclosed in "...", "unless it begins with one of the following special characters: dollar sign ($), at sign (@), single quotation mark ('), double quotation mark ("), or an opening parenthesis (()", according to Get-Help about_Parsing), then .$HOME should work the same way as, say, x$HOME.
I suspect the answer is that changing the behaviors described is way too late and risky, but perhaps the pitfalls of the existing behavior can be documented somewhere.
I came across this issue as I created a similar Unquoted Strings request at the The PowerShell Best Practices and Style Guide a few weeks ago without any activity. I have also created a similar statckoverflow question: Unquoted key rules / best practice, as I got lost in the (open and close) cascaded issues and whether this is already covered or even answered. And if not, to which issue I should add my findings (or create a new one?).
Activity
[-]Argument-mode parsing: Why is a subexpression at the start treated differently than a variable reference?[/-][+]Argument-mode parsing: Why is a subexpression at the start of an unquoted token treated differently than a variable reference?[/+]lzybkr commentedon Feb 27, 2017
Bruce Payette (@BrucePay) or James Truher (@JamesWTruher) can best answer this.
JamesWTruher commentedon May 24, 2017
if we look at what the parameter binder believes it's arguments are we can understand the output better:
vs
So, by the time the arguments get to the parameter binder it's already either broken up (or not) the tokens. There's definitely a difference between a variable reference
$aand a subexpression$(2+2)so i would expect a difference in the behavior betweenwrite-output $(2+2)/Handwrite-output $a/H.I suspect that it's the location of the obvious
stringelement which causes the behavioral change which changes it from two arguments to one. I haven't tracked that down though.write-output H/...is an obvious string (since that's how it starts), pretty much anything that follows it which isn't a token separator (spacefor example) is just going to be tacked on the end.As for
write-output .$HOME- remember that the.serves two functions, one as a invocation operator and another as a property reference operator, depending on context. In this case, we've definitely got 2 tokens.(the invocation operator) and$HOME, so we emit two lines.FWIW,
Write-Hostsees these two tokens as well:note the space. Why the difference? The difference between
write-hostandwrite-objectis the parameter type for the input object. inwrite-hostthe type isobjectand inwrite-outputthe parameter type isPSObject[].mklement0 commentedon May 24, 2017
Thanks for that, James Truher (@JamesWTruher).
To provide more motivation for my question, which is primarily about the user experience, not the technical underpinnings:
It is obscure to have behavior depend on the order of tokens in this case, especially since variable references and subexpressions are not treated differently when explicitly embedded inside
"..."Write-Output "$(2 + 2)/H"yields only 1 argument.In POSIX-like shells, a command/arithmetic substitution (the equivalent of a subexpression in PS) does work as the first token:
echo $(( 2 + 2 ))/Hyields 1 argument,4/HThat said, (the expanded results of) unquoted tokens in POSIX-like shells are subject to shell expansions that are often unwanted, leading to the annoyance that double quotes must be used everywhere (and beginners forgetting them is a constant source of bugs).
PowerShell generally frees us from that annoyance, so it's unfortunate that it doesn't do so in this case (because it results in multiple arguments).
Generally, remembering when it works and when it doesn't, in the absence of an intuitive reason, is a challenge and, I suspect, will be an ongoing source of confusion.
The same obscure behavior applies to juxtaposing differently quoted substrings (which are commonly used in POSIX-like shells):
Write-Output bare"I'm quoted"-> 1 argument, because the 1st token is unquoted:bareI'm quotedWrite-Output "I'm quoted"bare-> 2 arguments, because the 1st token is quoted:I'm quotedandbareAs for the
.$HOMEcase: Given that the token is a non-initial token parsed in argument mode, it shouldn't be interpreted as the invocation operator.If unquoted tokens are to be treated like expandable strings (i.e., as if the token were explicitly enclosed in
"...", "unless it begins with one of the following special characters: dollar sign ($), at sign (@), single quotation mark ('), double quotation mark ("), or an opening parenthesis (()", according toGet-Help about_Parsing), then.$HOMEshould work the same way as, say,x$HOME.I suspect the answer is that changing the behaviors described is way too late and risky, but perhaps the pitfalls of the existing behavior can be documented somewhere.
iRon7 commentedon Jul 6, 2020
I came across this issue as I created a similar Unquoted Strings request at the The PowerShell Best Practices and Style Guide a few weeks ago without any activity. I have also created a similar statckoverflow question: Unquoted key rules / best practice, as I got lost in the (open and close) cascaded issues and whether this is already covered or even answered. And if not, to which issue I should add my findings (or create a new one?).
mklement0 commentedon Jul 6, 2020
iRon7, #6467 is more comprehensive and there's a related docs issue now: MicrosoftDocs/PowerShell-Docs#3440