43

I've seen a number of example scripts online that use this. Most recently, I saw it in a script on automating TFS:

[string] $fields = "Title=$($taskTitle);Description=$($taskTitle);Assigned To=$($assignee);"
$fields += "Area Path=$($areaPath);Iteration Path=$($iterationPath);Discipline=$($taskDisciplineArray[$i]);Priority=$($i+1);"
$fields += "Estimate=$($taskEstimateArray[$i]);Remaining Work=$($taskRemainingArray[$i]);Completed Work=$($tasktaskCompletedArray[$i])"

From what I can tell, $($taskTitle) seems to be equivalent to $taskTitle. Am I missing something? Is there any reason to use the parentheses and extra dollar sign?

mklement0
  • 382,024
  • 64
  • 607
  • 775
KevinD
  • 3,023
  • 2
  • 22
  • 26
  • 4
    FYI in *this* case `"Area Path=$($areaPath);"` the parens are unnecessary. `"Area Path=$areaPath;"` would work equally well. That is, simple variable expansion just works within a double quoted string. You need the parens when you need to evaluate an expression like $($variable.property) or $($variable + 1). – Keith Hill Nov 29 '12 at 00:06

2 Answers2

46

The syntax helps with evaluating the expression inside it.

$arr = @(1,2,3)

$msg1 = "$arr.length"
echo $msg1 # prints 1 2 3.length - .length was treated as part of the string

$msg2 = "$($arr.length)"
echo $msg2 # prints 3

You can read more at http://ss64.com/ps/syntax-operators.html

mklement0
  • 382,024
  • 64
  • 607
  • 775
Amith George
  • 5,806
  • 2
  • 35
  • 53
27

To complement Amith George's helpful answer with more background information:

From what I can tell, $($taskTitle) seems to be equivalent to $taskTitle.

Indeed, in the context of "...", an expandable string (interpolating string):

  • You do NOT need $(...) with a simple variable reference such as $taskTitle or $env:HOME

    • Sometimes you must (or may choose to) use the form ${taskTitle} or ${env:HOME} - i.e., {...} around the identifier - so as to disambiguate the variable name from subsequent characters in the string.
  • You DO need $(...) for anything else:

    • accessing a property; e.g.:
      "count is: $($var.Count)"
    • embedding an expression; e.g.:
      "path prefix: $($var + '/')"
    • embedding entire commands (possibly even multiple ones); e.g.:
      "file names: $(Get-ChildItem *.txt | Select-Object -ExpandProperty Name)"

In short:

  • $(...) inside "..." is needed for anything other than simple variable references and allows you to embed entire statements inside "..."; as usual, when the string is evaluated, the $(...) part is replaced with the (stringified) output from the embedded statement(s).

  • If you don't want to think about when $(...) is and isn't needed, you can choose to always use it (e.g., $($taskTitle)), but note that it's cumbersome to type and visually "noisy".

    • Caveat: There is an edge case where the behavior of $($var) is not the same as that of $var / ${var}, namely if $var is a collection (implementing [System.Collections.IEnumerable]) that happens to contain only a single item - see PetSerAl's comments below.
  • Unless the referenced variable's / embedded statement's value already is a string, it is stringified using the .NET .ToString() method, with the notable twist that types that support culture-sensitive stringification are stringified with the invariant culture, which, loosely speaking, is like US-English format; e.g., "$(1.2)" always yields 1.2, even in cultures where , is the decimal mark; see this answer of mine for more.

Documentation:

The official name for $(...) is the subexpression operator, as (tersely) documented in Get-Help about_Operators, though the explanation there doesn't discuss the operator's specific use in the context of expandable strings.

Conversely, Get-Help about_Quoting_Rules, which discusses string literals including expandable strings, shows examples of $(...) use only in the context of expandable strings.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • 5
    BTW, `"${variable}"` and `"$($variable)"` not always return the same result. – user4003407 Feb 28 '18 at 16:12
  • 4
    Here is example: [gist](https://gist.github.com/PetSerAl/d6336e2bb76388af59e45f5942fb6935) or [tio.run](https://tio.run/##vY69CsIwFEb3@xSXkiEB6QMEHNog0tkxdIjlgkKaSJJSpPbZY/1DcBAnv@2Dc@Cc/EghHsjanDtrYsQKJe7OMVFfKm8tdenoXSybjRt6CmZvCSfAZfoLlXxocUvpfbl4arcFSkNwyFe6bqV0NHIhyg/8Ds8wAzy66r92qZ@7FE7LBWZwjbp6aQAFm8x8YZwZUeR8BQ) – user4003407 Feb 28 '18 at 16:22
  • 4
    As for explanation: it is direct consequences of difference between `${a}.GetType().FullName` and `$($a).GetType().FullName`; and how collection types stringified. – user4003407 Feb 28 '18 at 16:30
  • 4
    *this only seems to affect single-item collections, correct?* As far as I understand how it work, yes it is. To be equivalent of `"${variable}"` it need to be `"$(,$variable)"`. – user4003407 Feb 28 '18 at 16:40