I am a PowerShell novice. Suppose that I wish to call
Get-Service -Name winrm, netlogon
Due to parameter binding, this will be parsed as if we typed @("winrm", "netlogon")
rather than winrm, netlogon
. But what is the precedence of parameter binding? Can anything happen before it? The documentation lists the comma operator as having very high precedence. But, somehow, parameter binding beats it.
Operator precedence in PowerShell only applies to expressions, i.e. code parsed in expression (parsing) mode.
By contrast, any call to a command (such as Get-Service
) is parsed in argument (parsing) mode.
In this mode, the overall parsing logic is based on invoking a command (a named unit of executable code, which may be a PowerShell command (alias, cmdlet, function, script block), a PowerShell script file (*.ps1
), or an external program) with zero or more (potentially named) arguments.
,
- the array constructor ("comma") operator - is the only operator that is directly recognized in this mode and only in the context of a given command argument.
Unlike in expression mode, its operands (array elements) - just like stand-alone arguments - may be passed as so-called barewords, i.e. as unquoted strings, as long as they don't contain any (unescaped) metacharacters, notably spaces.
However, you're free to use expressions as arguments via (...)
, the grouping operator, which also accepts a command (pipeline); you may even pass (the output from) entire statements via $(...)
, the subexpression operator, or @(...)
, the array-subexpression operator.
Irrespective of how a command argument is passed - whether as a (potentially expandable, i.e. interpolating) bareword, a (potentially expandable) string literal, via (...)
, $(...)
, or @(...)
- it is evaluated up front, which in the case of embedded commands or statements means that they are run to completion, before the (potentially collected) result(s) are passed to the target command.
In argument mode, the ,
operator allows you to construct an array from two or more of any of the above-mentioned constructs; let's call them <token>
in this discussion:
<token>, <token>[, ...]
- whitespace around ,
is optional.Given that argument winrm, netlogon
is passed to a PowerShell command, it is indeed equivalent to the following expression: @('winrm', 'netlogon')
, i.e. it constructs a 2-element array whose elements are strings.[1]
By contrast, an external program would see this as two arguments, namely verbatim winrm,
and netlogon
.[2]
If you want any given command to receive winrm, netlogon
as a single string, use quoting, e.g. a verbatim string, '...'
, i.e. 'winrm, netlogon'
; if you need string interpolation (expansion), use an expandable string, "..."
, e.g. "$serviceName1, netlogon"
If you want to use ,
verbatim as part of a bareword, use `
, the so-called backtick, PowerShell's escape character, e.g. winrm`,
For a comprehensive overview of how unquoted command arguments are parsed in argument mode, see this answer.
[1] But note that it depends on the type of the parameter of the target command that the argument binds whether an array is accepted as such. See this answer for more information.
[2] This applies to PowerShell (Core) 7; in Windows PowerShell (the legacy, ships-with-Windows, Windows-only edition of PowerShell whose latest and last version is 5.1), the trailing ,
is _stripped from winrm
. By contrast, if you were to pass winrm,netlogin
, i.e. a token without spaces, verbatim winrm,netlogin
would be passed in both editions. Another notably difference - which is regrettable owed to a bug - is that PowerShell (Core) 7 neglects to expand (interpolate) the ,
-separated elements when calling external programs, even though it should, and does so both in isolation and when calling PowerShell commands; e.g., passing $HOME
alone to an external program passes the value of said variable in both editions, but passing $HOME, foo
does not result in expansion of $HOME
in PowerShell 7 as of v7.5.x. See GitHub issue #18502 for the relevant bug report.