tl;dr
Inside "...":
$(...)is only needed to embed entire expressions or commands (e.g.")PSVersionTable.PSVersion)"
${...}is only needed if a stand-alone reference to a variable with a non-special name needs delimiting (disambiguation), so that subsequent characters in the string aren't interpreted as part of the variable name (e.g,"${foo}_bar"correctly embeds variable$foo; without{...}, PowerShell would look for variable$foo_bar; notably, a subsequent:requires this technique too:"${foo}:bar"; by contrast, something like"$foo!"also works without{...})
Independently of use in "...", ${...} is also needed for variable names containing special characters (e.g. ${a-b} = 42)
For a comprehensive overview of PowerShell's expandable strings (string interpolation), see this answer.
Background information:
To complement marsze's helpful answer:
${...} (enclosing the variable name in { and }) is indeed always necessary if a variable name contains special characters, such as spaces, . or -.
Not special are
_and - surprisingly and problematically -?.- For the exact rules, see the relevant section of the conceptual "about_Variables" help topic, Variable Names that Include Special Characters.
(The first occurrence of)
:in a name is invariably interpreted as terminating either a PowerShell drive reference - in the context of namespace variable notation - or a scope specifier, irrespective of whether{...}enclosure is used or required (e.g., in$env:USERNAMEor${env:USERNAME},envrefers to theEnv:PowerShell drive representing all environment variables; in$script:fooor${script:foo},scriptrefers to the script's scope and its variables).
Note:
${...}- the syntax for disambiguating a variable name - is not to be confused with$(...), the subexpression operator, which is needed to embed any expression or command that goes beyond a stand-alone variable reference inside"...", an expandable (interpolating) string.As such, these two syntax forms are independent of one another and may need to be combined in a given situation; e.g.
"$var"/"${var}"work fine, but"$var.someProperty"/"${var}.someProperty"do not: you need"/var.someProperty)"
"{var}.someProperty)"
In the context of "...", there is another reason to use ${...}, even if the variable name itself doesn't need it:
If you need to delineate the variable name from directly following non-whitespace characters, notably including ::
$foo = 'bar' # example variable
# INCORRECT: PowerShell assumes that the variable name is 'foobarian', not 'foo'
PS> "A $foobarian."
A . # Variable $foobarian doesn't exist -> reference expanded to empty string.
# CORRECT: Use {...} to delineate the variable name:
PS> "A ${foo}barian."
A barbarian.
# INCORRECT: PowerShell assumes that 'foo:' is a *namespace* (drive) reference
# (such as 'env:' in $env:PATH) and FAILS:
PS> "$foo: bar"
Variable reference is not valid. ':' was not followed by a valid variable name character.
Consider using ${} to delimit the name.
# CORRECT: Use {...} to delineate the variable name:
PS> "${foo}: bar"
bar: bar
See this answer for a comprehensive overview of PowerShell string-expansion rules.
Note that you need the same technique when string expansion is implicitly applied, in the context of passing an unquoted argument to a command; e.g.:
# INCORRECT: The argument is treated as if it were enclosed in "...",
# so the same rules apply.
Write-Output $foo:/bar
# CORRECT
Write-Output ${foo}:/bar
Finally, a somewhat obscure alternative is to `-escape the first character after the variable name, but the problem is that this only works as expected with characters that aren't part of escape sequences (see about_Special_Characters):
# OK: because `: is not an escape sequence.
PS> "$foo`: bar"
bar: bar
# NOT OK, because `b is the escape sequence for a backspace character.
PS> "$foo`bar"
baar # The `b "ate" the trailing 'r' of the variable value
# and only "ar" was the literal part.
Answer from mklement0 on Stack Overflowtl;dr
Inside "...":
$(...)is only needed to embed entire expressions or commands (e.g.")PSVersionTable.PSVersion)"
${...}is only needed if a stand-alone reference to a variable with a non-special name needs delimiting (disambiguation), so that subsequent characters in the string aren't interpreted as part of the variable name (e.g,"${foo}_bar"correctly embeds variable$foo; without{...}, PowerShell would look for variable$foo_bar; notably, a subsequent:requires this technique too:"${foo}:bar"; by contrast, something like"$foo!"also works without{...})
Independently of use in "...", ${...} is also needed for variable names containing special characters (e.g. ${a-b} = 42)
For a comprehensive overview of PowerShell's expandable strings (string interpolation), see this answer.
Background information:
To complement marsze's helpful answer:
${...} (enclosing the variable name in { and }) is indeed always necessary if a variable name contains special characters, such as spaces, . or -.
Not special are
_and - surprisingly and problematically -?.- For the exact rules, see the relevant section of the conceptual "about_Variables" help topic, Variable Names that Include Special Characters.
(The first occurrence of)
:in a name is invariably interpreted as terminating either a PowerShell drive reference - in the context of namespace variable notation - or a scope specifier, irrespective of whether{...}enclosure is used or required (e.g., in$env:USERNAMEor${env:USERNAME},envrefers to theEnv:PowerShell drive representing all environment variables; in$script:fooor${script:foo},scriptrefers to the script's scope and its variables).
Note:
${...}- the syntax for disambiguating a variable name - is not to be confused with$(...), the subexpression operator, which is needed to embed any expression or command that goes beyond a stand-alone variable reference inside"...", an expandable (interpolating) string.As such, these two syntax forms are independent of one another and may need to be combined in a given situation; e.g.
"$var"/"${var}"work fine, but"$var.someProperty"/"${var}.someProperty"do not: you need"/var.someProperty)"
"{var}.someProperty)"
In the context of "...", there is another reason to use ${...}, even if the variable name itself doesn't need it:
If you need to delineate the variable name from directly following non-whitespace characters, notably including ::
$foo = 'bar' # example variable
# INCORRECT: PowerShell assumes that the variable name is 'foobarian', not 'foo'
PS> "A $foobarian."
A . # Variable $foobarian doesn't exist -> reference expanded to empty string.
# CORRECT: Use {...} to delineate the variable name:
PS> "A ${foo}barian."
A barbarian.
# INCORRECT: PowerShell assumes that 'foo:' is a *namespace* (drive) reference
# (such as 'env:' in $env:PATH) and FAILS:
PS> "$foo: bar"
Variable reference is not valid. ':' was not followed by a valid variable name character.
Consider using ${} to delimit the name.
# CORRECT: Use {...} to delineate the variable name:
PS> "${foo}: bar"
bar: bar
See this answer for a comprehensive overview of PowerShell string-expansion rules.
Note that you need the same technique when string expansion is implicitly applied, in the context of passing an unquoted argument to a command; e.g.:
# INCORRECT: The argument is treated as if it were enclosed in "...",
# so the same rules apply.
Write-Output $foo:/bar
# CORRECT
Write-Output ${foo}:/bar
Finally, a somewhat obscure alternative is to `-escape the first character after the variable name, but the problem is that this only works as expected with characters that aren't part of escape sequences (see about_Special_Characters):
# OK: because `: is not an escape sequence.
PS> "$foo`: bar"
bar: bar
# NOT OK, because `b is the escape sequence for a backspace character.
PS> "$foo`bar"
baar # The `b "ate" the trailing 'r' of the variable value
# and only "ar" was the literal part.
Note that $() is helpful for json objects:
"My json property is
jsonObj.property)"
Support formatting specifiers in string interpolation
Add syntax for a fast-fail/strict expansion to string interpolation
Support Named Token String Replacement
Come up with a better concat/string interpolation strategy
Videos
Hi everyone,
I'm certain PowerShell has this feature, but I can't remember the name of it.
I'm looking for formatting string using interpolation but where the string is interpolated at the time of reading it rather than at creation. In doing so, it takes the current value of the included variables rather than being stuck on the past contents of the variables. I think Python's f strings are what I'm after, but I could be wrong.
Any ideas?
PowerShell only supports interpolation at the time of a variable creation, unless you are talking about calculated object properties:
https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_calculated_properties?view=powershell-7
If you are looking for f-strings see here: https://ss64.com/ps/syntax-f-operator.html
Probably $ExecutionContext.InvokeCommand.ExpandString('$Host') but it's super finicky and potentially dangerous. It's not recommended.
tl;dr
Inside "...":
$(...)is only needed to embed entire expressions or commands (e.g."$($PSVersionTable.PSVersion)")${...}is only needed if a stand-alone reference to a variable with a non-special name needs delimiting (disambiguation), so that subsequent characters in the string aren't interpreted as part of the variable name (e.g,"${foo}_bar"correctly embeds variable$foo; without{...}, PowerShell would look for variable$foo_bar; notably, a subsequent:requires this technique too:"${foo}:bar"; by contrast, something like"$foo!"also works without{...})
Independently of use in "...", ${...} is also needed for variable names containing special characters (e.g. ${a-b} = 42)
For a comprehensive overview of PowerShell's expandable strings (string interpolation), see this answer.
Background information:
To complement marsze's helpful answer:
${...} (enclosing the variable name in { and }) is indeed always necessary if a variable name contains special characters, such as spaces, . or -.
Not special are
_and - surprisingly and problematically -?.- For the exact rules, see the relevant section of the conceptual "about_Variables" help topic, Variable Names that Include Special Characters.
(The first occurrence of)
:in a name is invariably interpreted as terminating either a PowerShell drive reference - in the context of namespace variable notation - or a scope specifier, irrespective of whether{...}enclosure is used or required (e.g., in$env:USERNAMEor${env:USERNAME},envrefers to theEnv:PowerShell drive representing all environment variables; in$script:fooor${script:foo},scriptrefers to the script's scope and its variables).
Note:
${...}- the syntax for disambiguating a variable name - is not to be confused with$(...), the subexpression operator, which is needed to embed any expression or command that goes beyond a stand-alone variable reference inside"...", an expandable (interpolating) string.As such, these two syntax forms are independent of one another and may need to be combined in a given situation; e.g.
"$var"/"${var}"work fine, but"$var.someProperty"/"${var}.someProperty"do not: you need"$($var.someProperty)"/"$(${var}.someProperty)"
In the context of "...", there is another reason to use ${...}, even if the variable name itself doesn't need it:
If you need to delineate the variable name from directly following non-whitespace characters, notably including ::
$foo = 'bar' # example variable
# INCORRECT: PowerShell assumes that the variable name is 'foobarian', not 'foo'
PS> "A $foobarian."
A . # Variable $foobarian doesn't exist -> reference expanded to empty string.
# CORRECT: Use {...} to delineate the variable name:
PS> "A ${foo}barian."
A barbarian.
# INCORRECT: PowerShell assumes that 'foo:' is a *namespace* (drive) reference
# (such as 'env:' in $env:PATH) and FAILS:
PS> "$foo: bar"
Variable reference is not valid. ':' was not followed by a valid variable name character.
Consider using ${} to delimit the name.
# CORRECT: Use {...} to delineate the variable name:
PS> "${foo}: bar"
bar: bar
See this answer for a comprehensive overview of PowerShell string-expansion rules.
Note that you need the same technique when string expansion is implicitly applied, in the context of passing an unquoted argument to a command; e.g.:
# INCORRECT: The argument is treated as if it were enclosed in "...",
# so the same rules apply.
Write-Output $foo:/bar
# CORRECT
Write-Output ${foo}:/bar
Finally, a somewhat obscure alternative is to `-escape the first character after the variable name, but the problem is that this only works as expected with characters that aren't part of escape sequences (see about_Special_Characters):
# OK: because `: is not an escape sequence.
PS> "$foo`: bar"
bar: bar
# NOT OK, because `b is the escape sequence for a backspace character.
PS> "$foo`bar"
baar # The `b "ate" the trailing 'r' of the variable value
# and only "ar" was the literal part.
Note that $() is helpful for json objects:
"My json property is $($jsonObj.property)"
Is this supported?
No, formatting is not supported during string expansion
As you might have noticed, string expansion in PowerShell works by naively resolving subexpressions nested in double-quoted strings - there are no {} placeholder constructs.
If you want string formatting, -f is the way to go.
FWIW, $s -f $a is directly translated to a String.Format($s, $a) call
For value types that support string formatting you can usually also call ToString() with a format string (just like in C#):
PS C:\> $a = 1 / 3
PS C:\> $a.ToString("F2")
0.33
The way to go with this is to use the -f operator. The -f operator takes a format string as it's first operand and the items you want to format as the second. The format string must contain tokens like {n:f} where n is the index of the item in the items to be formatted and f is the format specifier. For example {0:D4} will take the first item and format it as a Decimal that is a minimum of 4 digits. So '{0:D4}-{1:D3}' -f 24,3 gives the output 0024-003. ref
Inside an expandable string this can be used within a subexpression operator $().
A real world example is to split an input file into multiple files with ten lines in each while making the the output file name contain a serial number wich is padded with zeros to make it four digits. This can be done like this:
$i = 0; Get-Content input.txt -ReadCount 10 | %{ $i++; $_ | Out-File "output_$('{0:D4}' -f $i).txt" }
Creates files
output_0001.txt
output_0002.txt
...