9

I have a simple question regarding why something works the way it does and I cant seem to readily find out why. I was trying to run the following command:

foreach($a in $list){set-mailboxcalendarpermissions -identity $($a):\calendar

while it works just fine, I don't know what adding the $( ) actually does. When I do ($a):\calendar it would return (variable):\calendar with the parenthesis, but adding the extra "$" fixes it. why?

Thank you all for your help with this terribly worded question.

3 Answers 3

15

$() is a subexpression operator. It means "evaluate this first, and do it separately as an independent statement".

Most often, its used when you're using an inline string. Say:

$x = Get-ChildItem C:\;
$x | ForEach-Object {
    Write-Output "The file is $($_.FullName)";
}

Compare that to:

$x = Get-ChildItem C:\;
$x | ForEach-Object {
    Write-Output "The file is $_.FullName";
}

You can also do things like $($x + $y).ToString(), or $(Get-Date).AddDays(10).

Here, without the subexpression, you'd get $a:\calendar. Well, the problem there is that the colon after a variable is an operator. Specifically, the scope operator. To keep PowerShell from thinking you're trying to look for a variable in the a namespace, the author put the variable in a subexpression.

As far as I've been able to tell using PS for the past few years, parentheses without the dollar sign are also essentially subexpressions. They won't be evaluated as a subexpression when within a string, but otherwise they usually will. It's kind of a frustrating quirk that there's no clear difference.

Sign up to request clarification or add additional context in comments.

2 Comments

You could also mention that a simpler way to prevent the colon being interpreted as a namespace separator would have been to write ${a}:\calendar
BTW, the main difference between (pipeline) and $(statement-list) is that the $() contains a statement-list which includes if, while etc. as well as a pipeline, but parentheses need a pipeline, i.e. an assignment, expression, or command. Also in strings only $() has meaning.
11

The $() is the subexpression operator. It causes the contained expressions to be evaluated and it returns all expressions as an array (if there is more than one) or as a scalar (single value). Plain parentheses () are simply the mathematical precedence operator and therefore just work in arithmetic expressions to give precedence

Notably, $() is interpreted within a string, whereas () will be just be taken literally - it has no special meaning. So the following:

$a = "Hello"
"$($a.Length)"

gives

5

whereas

"($a.Length)"

gives

"(Hello.Length)"

As I said, the $() can consist of multiple expressions, all of which are returned as output. () does not do this. So this is an error as the contents are not an arithmetic expression:

(1;2)

whereas this

$(1;2)

evaluates to an array and outputs:

1
2

The expression $($a) is evaluated before the application of the trailing scope : operator and prevents the scope being applied directly to a, instead $a is evaluated first and then the scope is applied.

Comments

0

To add to the preexisting, helpful answers:

[...] $($a):\calendar while it works just fine ...

It actually doesn't: Unless you enclose $($a):\calendar as a command argument in "...", the value of $($a) and the literal :\calendar are passed as separate arguments.

  • A simple repro: Write-Output $($HOME):\foo: the value of $HOME and :\foo print on separate lines, because Write-Output sees them as separate arguments.

  • This perhaps surprising behavior is discussed in GitHub issue #6467.


  • The $(...) enclosure in $($a):\calendar is an apparent attempt to avoid the error that would result from $a:\calendar, in which case the : would be interpreted as a scope / namespace separator, with a getting interpreted as a scope / namespace name.

  • While this workaround is effective - inside "...", as noted - it is both syntactically cumbersome and inefficient, as it creates a nested pipeline:

    • Enclosing a variable name in {...} is sufficient to disambiguate it from subsequent characters that would by default be considered part of the same variable reference, as Duncan notes:

      ${a}:\calendar
      
      • Note that "..." enclosure is not strictly necessary in this case.

$(...), the subexpression operator, is indeed primarily useful inside "...", i.e. expandable (interpolating) strings:

  • In command arguments, i.e., in argument-parsing mode, barewords, i.e. unquoted arguments are situationally treated implicitly like expandable strings, but - for the reasons discussed in the linked GitHub issue - result in separate arguments if the $(...) operation comes first.

  • As for distinguishing between $(...) (and, similarly, @(...), the array-subexpression operator) vs. (...), the grouping operator, again building on Duncan's comments:

    • Among these three operators, only $(...) can be used inside (potentially implicitly) expandable strings ("...")
    • Only $(...) and @(...) create a nested pipeline and can include language statements such as if and foreach and support multiple, ;-separated statements.
    • (...) can only contain a single expression or pipeline.
    • See this answer for additional information.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.