about_Parsing
Short description
Describes how PowerShell parses commands.
Long description
When you enter a command at the command prompt, PowerShell breaks the command text into a series of segments called tokens and then determines how to interpret each token.
For example, if you type:
Write-Host book
PowerShell breaks the command into two tokens, Write-Host
and book
, and
interprets each token independently using one of two major parsing modes:
expression mode and argument mode.
Note
As PowerShell parses command input it tries to resolve the command names to
cmdlets or native executables. If a command name doesn't have an exact
match, PowerShell prepends Get-
to the command as a default verb. For
example, PowerShell parses Service
as Get-Service
. It's not
recommended to use this feature for the following reasons:
- It's inefficient. This causes PowerShell to search multiple times.
- External programs with the same name are resolved first, so you may not execute the intended cmdlet.
Get-Help
andGet-Command
don't recognize verb-less names.- The command name may be a reserved word or a language keyword.
Process
is both, and can't be resolved toGet-Process
.
Expression mode
Expression mode is intended for combining expressions, required for value manipulation in a scripting language. Expressions are representations of values in PowerShell syntax, and can be simple or composite, for example:
Literal expressions are direct representations of their values:
'hello'
32
Variable expressions carry the value of the variable they reference:
$x
$script:path
Operators combine other expressions for evaluation:
-12
-not $Quiet
3 + 7
$input.Length -gt 1
- Character string literals must be contained in quotation marks.
- Numbers are treated as numerical values rather than as a series of characters (unless escaped).
- Operators, including unary operators like
-
and-not
and binary operators like+
and-gt
, are interpreted as operators and apply their respective operations on their arguments (operands). - Attribute and conversion expressions are parsed as expressions and applied
to subordinate expressions. For example:
[int] '7'
. - Variable references are evaluated to their values but splatting is forbidden and causes a parser error.
- Anything else is treated as a command to be invoked.
Argument mode
When parsing, PowerShell first looks to interpret input as an expression. But when a command invocation is encountered, parsing continues in argument mode. If you have arguments that contain spaces, such as paths, then you must enclose those argument values in quotes.
Argument mode is designed for parsing arguments and parameters for commands in a shell environment. All input is treated as an expandable string unless it uses one of the following syntaxes:
Dollar sign (
$
) followed by a variable name begins a variable reference, otherwise it's interpreted as part of the expandable string. The variable reference can include member access or indexing.- Additional characters following simple variable references, such as
$HOME
, are considered part of the same argument. Enclose the variable name in braces ({}
) to separate it from subsequent characters. For example,${HOME}
. - When the variable reference includes member access, the first of any
additional characters is considered the start of a new argument. For
example
$HOME.Length-more
results in two arguments: the value of$HOME.Length
and string literal-more
.
- Additional characters following simple variable references, such as
Quotation marks (
'
and"
) begin stringsBraces (
{}
) begin a new script blocksCommas (
,
) introduce lists passed as arrays, unless the command being called is a native application, in which case they're interpreted as part of the expandable string. Initial, consecutive or trailing commas aren't supported.Parentheses (
()
) begin a new expressionSubexpression operator (
$()
) begins an embedded expressionInitial at sign (
@
) begins expression syntaxes such as splatting (@args
), arrays (@(1,2,3)
), and hash table literals (@{a=1;b=2}
).()
,$()
, and@()
at the start of a token create a new parsing context that can contain expressions or nested commands.- When followed by additional characters, the first additional character is considered the start of a new, separate argument.
- When preceded by an unquoted literal
$()
works like an expandable string,()
starts a new argument that's an expression, and@()
is taken as literal@
with()
starting a new argument that's an expression.
Everything else is treated as an expandable string, except metacharacters that still need escaping. See Handling special characters.
- The argument-mode metacharacters (characters with special syntactic
meaning) are:
<space> ' " ` , ; ( ) { } | & < > @ #
. Of these,< > @ #
are only special at the start of a token.
- The argument-mode metacharacters (characters with special syntactic
meaning) are:
The stop-parsing token (
--%
) changes the interpretation of all remaining arguments. For more information, see the stop-parsing token section below.
Examples
The following table provides several examples of tokens processed in expression
mode and argument mode and the evaluation of those tokens. For these examples,
the value of the variable $a
is 4
.
Example | Mode | Result |
---|---|---|
2 |
Expression | 2 (integer) |
`2 |
Expression | "2" (command) |
Write-Output 2 |
Expression | 2 (integer) |
2+2 |
Expression | 4 (integer) |
Write-Output 2+2 |
Argument | "2+2" (string) |
Write-Output(2+2) |
Expression | 4 (integer) |
$a |
Expression | 4 (integer) |
Write-Output $a |
Expression | 4 (integer) |
$a+2 |
Expression | 6 (integer) |
Write-Output $a+2 |
Argument | "4+2" (string) |
$- |
Argument | "$-" (command) |
Write-Output $- |
Argument | "$-" (string) |
a$a |
Expression | "a$a" (command) |
Write-Output a$a |
Argument | "a4" (string) |
a'$a' |
Expression | "a$a" (command) |
Write-Output a'$a' |
Argument | "a$a" (string) |
a"$a" |
Expression | "a$a" (command) |
Write-Output a"$a" |
Argument | "a4" (string) |
a$(2) |
Expression | "a$(2)" (command) |
Write-Output a$(2) |
Argument | "a2" (string) |
Every token can be interpreted as some kind of object type, such as Boolean or String. PowerShell attempts to determine the object type from the expression. The object type depends on the type of parameter a command expects and on whether PowerShell knows how to convert the argument to the correct type. The following table shows several examples of the types assigned to values returned by the expressions.
Example | Mode | Result |
---|---|---|
Write-Output !1 |
argument | "!1" (string) |
Write-Output (!1) |
expression | False (Boolean) |
Write-Output (2) |
expression | 2 (integer) |
Set-Variable AB A,B |
argument | 'A','B' (array) |
CMD /CECHO A,B |
argument | 'A,B' (string) |
CMD /CECHO $AB |
expression | 'A B' (array) |
CMD /CECHO :$AB |
argument | ':A B' (string) |
Handling special characters
The backtick character (`
) can be used to escape any special character in
an expression. This is most useful for escaping the argument-mode
metacharacters that you want to use as literal characters rather than as a
metacharacter. For example, to use the dollar sign ($
) as a literal in an
expandable string:
"The value of `$ErrorActionPreference is '$ErrorActionPreference'."
The value of $ErrorActionPreference is 'Continue'.
Line continuation
The backtick character can also be used at the end of a line to allow you to continue the input on the next line. This is improve the readability of a command that takes several parameters with long names and argument values. For example:
New-AzVm `
-ResourceGroupName "myResourceGroupVM" `
-Name "myVM" `
-Location "EastUS" `
-VirtualNetworkName "myVnet" `
-SubnetName "mySubnet" `
-SecurityGroupName "myNetworkSecurityGroup" `
-PublicIpAddressName "myPublicIpAddress" `
-Credential $cred
However, you should avoid using line continuation.
- The backtick characters can be hard to see and easy to forget.
- An extra space after the backtick breaks the line continuation. Since the space is hard to see it can be difficult to find the error.
PowerShell provides several ways break lines at natural points in the syntax.
- After pipe characters (
|
) - After binary operators (
+
,-
,-eq
, etc.) - After commas (
,
) in an array - After opening characters such as
[
,{
,(
For large parameter set, use splatting instead. For example:
$parameters = @{
ResourceGroupName = "myResourceGroupVM"
Name = "myVM"
Location = "EastUS"
VirtualNetworkName = "myVnet"
SubnetName = "mySubnet"
SecurityGroupName = "myNetworkSecurityGroup"
PublicIpAddressName = "myPublicIpAddress"
Credential = $cred
}
New-AzVm @parameters
Passing arguments to native commands
When running native commands from PowerShell, the arguments are first parsed by PowerShell. The parsed arguments are then joined into a single string with each parameter separated by a space.
For example, the following command calls the icacls.exe
program.
icacls X:\VMS /grant Dom\HVAdmin:(CI)(OI)F
To run this command in PowerShell 2.0, you must use escape characters to prevent PowerShell from misinterpreting the parentheses.
icacls X:\VMS /grant Dom\HVAdmin:`(CI`)`(OI`)F
The stop-parsing token
Beginning in PowerShell 3.0, you can use the stop-parsing (--%
) token to
stop PowerShell from interpreting input as PowerShell commands or expressions.
Note
The stop-parsing token is only intended for use native commands on Windows platforms.
When calling a native command, place the stop-parsing token before the program arguments. This technique is much easier than using escape characters to prevent misinterpretation.
When it encounters a stop-parsing token, PowerShell treats the remaining
characters in the line as a literal. The only interpretation it performs is to
substitute values for environment variables that use standard Windows notation,
such as %USERPROFILE%
.
icacls X:\VMS --% /grant Dom\HVAdmin:(CI)(OI)F
PowerShell sends the following command string to the icacls.exe
program:
X:\VMS /grant Dom\HVAdmin:(CI)(OI)F
The stop-parsing token is effective only until the next newline or pipeline
character. You can't use the line continuation character (`
) to extend
its effect or use a command delimiter (;
) to terminate its effect.
Other than %variable%
environment-variable references, you can't embed any
other dynamic elements in the command. Escaping a %
character as %%
, the
way you can do inside batch files, isn't supported. %<name>%
tokens are
invariably expanded. If <name>
doesn't refer to a defined environment
variable the token is passed through as-is.
You can't use stream redirection (like >file.txt
) because they're passed
verbatim as arguments to the target command.
In the following example, the first step runs a command without using the
stop-parsing token. PowerShell evaluates the quoted string and passes the value
(without quotes) to cmd.exe
, which results in an error.
PS> cmd /c echo "a|b"
'b' is not recognized as an internal or external command,
operable program or batch file.
PS> cmd /c --% echo "a|b"
"a|b"
Note
The stop-parsing token isn't needed when using PowerShell cmdlets. However, it could be useful to pass arguments to a PowerShell function that is designed to call a native command with those arguments.
Passing arguments that contain quote characters
Some native commands expect arguments that contain quote characters. PowerShell 7.3 changed the way the command line is parsed for native commands.
Caution
The new behavior is a breaking change from the Window PowerShell 5.1
behavior. This may break scripts and automation that work around the various
issues when invoking native applications. Use the stop-parsing token (--%
)
or the Start-Process
cmdlet to avoid the native argument passing when
needed.
The new $PSNativeCommandArgumentPassing
preference variable controls this
behavior. This variable allows you to select the behavior at runtime. The valid
values are Legacy
, Standard
, and Windows
. The default behavior is
platform specific. On Windows platforms, the default setting is Windows
and
non-Windows platforms default to Standard
.
Legacy
is the historic behavior. The behavior of Windows
and Standard
mode are the same except, in Windows
mode, invocations of the following files
automatically use the Legacy
style argument passing.
cmd.exe
cscript.exe
wscript.exe
- ending with
.bat
- ending with
.cmd
- ending with
.js
- ending with
.vbs
- ending with
.wsf
If the $PSNativeCommandArgumentPassing
is set to either Legacy
or
Standard
, the parser doesn't check for these files.
Note
The following examples use the TestExe.exe
tool. You can build TestExe
from the source code. See TestExe in the PowerShell source repository.
New behaviors made available by this change:
Literal or expandable strings with embedded quotes the quotes are now preserved:
PS> $a = 'a" "b' PS> TestExe -echoargs $a 'c" "d' e" "f Arg 0 is <a" "b> Arg 1 is <c" "d> Arg 2 is <e f>
Empty strings as arguments are now preserved:
PS> TestExe -echoargs '' a b '' Arg 0 is <> Arg 1 is <a> Arg 2 is <b> Arg 3 is <>
The goal of these examples is to pass the directory path (with spaces and
quotes) "C:\Program Files (x86)\Microsoft\"
to a native command so that it
received the path as a quoted string.
In Windows
or Standard
mode, the following examples produce the expected
results:
TestExe -echoargs """${env:ProgramFiles(x86)}\Microsoft\"""
TestExe -echoargs '"C:\Program Files (x86)\Microsoft\"'
To get the same results in Legacy
mode, you must escape the quotes or use the
stop-parsing token (--%
):
TestExe -echoargs """""${env:ProgramFiles(x86)}\Microsoft\\"""""
TestExe -echoargs "\""C:\Program Files (x86)\Microsoft\\"""
TestExe -echoargs --% ""\""C:\Program Files (x86)\Microsoft\\"\"""
TestExe -echoargs --% """C:\Program Files (x86)\Microsoft\\""
TestExe -echoargs --% """%ProgramFiles(x86)%\Microsoft\\""
Note
The backslash (\
) character isn't recognized as an escape character by
PowerShell. It's the escape character used by the underlying API for
ProcessStartInfo.ArgumentList.
PowerShell 7.3 also added the ability to trace parameter binding for native commands. For more information, see Trace-Command.
Passing arguments to PowerShell commands
Beginning in PowerShell 3.0, you can use the end-of-parameters token (--
)
to stop PowerShell from interpreting input as PowerShell parameters. This is a
convention specified in the POSIX Shell and Utilities specification.
The end-of-parameters token
The end-of-parameters token (--
) indicates that all arguments following it
are to be passed in their actual form as though double quotes were placed
around them. For example, using --
you can output the string -InputObject
without using quotes or having it interpreted as a parameter:
Write-Output -- -InputObject
-InputObject
Unlike the stop-parsing (--%
) token, any values following the --
token can
be interpreted as expressions by PowerShell.
Write-Output -- -InputObject $env:PROCESSOR_ARCHITECTURE
-InputObject
AMD64
This behavior only applies to PowerShell commands. If you use the --
token
when calling an external command, the --
string is passed as an argument to
that command.
TestExe -echoargs -a -b -- -c
The output shows that --
is passed as an argument to TestExe
.
Arg 0 is <-a>
Arg 1 is <-b>
Arg 2 is <-->
Arg 3 is <-c>
Tilde (~)
The tilde character (~
) has special meaning in PowerShell. When it's used
with PowerShell commands at the beginning of a path, the tilde character is
expanded to the user's home directory. If the tilde character is used anywhere
else in a path, it's treated as a literal character.
PS D:\temp> $PWD
Path
----
D:\temp
PS D:\temp> Set-Location ~
PS C:\Users\user2> $PWD
Path
----
C:\Users\user2
In this example, the Name parameter of the New-Item
expects a string. The
tilde character is treated as a literal character. To change to the newly
created directory, you must qualify the path with the tilde character.
PS D:\temp> Set-Location ~
PS C:\Users\user2> New-Item -Type Directory -Name ~
Directory: C:\Users\user2
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 5/6/2024 2:08 PM ~
PS C:\Users\user2> Set-Location ~
PS C:\Users\user2> Set-Location .\~
PS C:\Users\user2\~> $PWD
Path
----
C:\Users\user2\~
When you use the tilde character with native commands, PowerShell passes the tilde as a literal character. Using the tilde in a path causes errors for native commands on Windows that don't support the tilde character.
PS D:\temp> $PWD
Path
----
D:\temp
PS D:\temp> Get-Item ~\repocache.clixml
Directory: C:\Users\user2
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 4/29/2024 3:42 PM 88177 repocache.clixml
PS D:\temp> more.com ~\repocache.clixml
Cannot access file D:\temp\~\repocache.clixml