Using subexpressions in expanding string

Sub expressions can be used in two general situations. The first and the important one is to mark a part of quoted string that should be expanded. If you place expanding “string” in PowerShell script the interpreter checks if there is anything to expand, it looks for variable names and subexpression if any are found they get invoked (expanded). As you can see this script does not use any subexpressions but still the variable ‘win’ gets expanded:

$win = Get-Item  $env:windir
"Path to the Windows directory is $win."
Path to the Windows directory is C:\Windows.

Now let’s try to find out when the directory was created looking at the CreationTime property:

"The folder was created on $win.CreationTime."
The folder was created on C:\Windows.CreationTime

Not the result we wanted, but if you think about it the behavior is reasonable because the variable name could be followed by anything. To explicitly mark the part of the string that should be expanded use the subexpression sign as follows:

"The folder was created on $($win.CreationTime)."
The folder was created on 07/14/2009 04:37:05.

That’s more like it.

In the sub expression you can use more than one command and also more pipelines, as shown in this example:

"The first and the last processes are $($first = Get-Process |
	select -First 1 -ExpandProperty Name ; $last = Get-Process |
		select -Last 1  -ExpandProperty Name; $first, $last -join ", ")."
The first and the last processes are armsvc, wuauclt.

Useful but if you look at it, it makes the code unreadable and the syntax is not highlighted in IDE. It is best to avoid passing long commands to the subexpression; in some cases you can use the formatting operator ‘-f’ instead or process the command before, save its output to a variable and pass the variable to the string.

From my point of view this usage of sub expressions is the only one should’t make you stop and think: “Why am I doing it this way?”

Process more commands in one line

The other case is to pass more that one command as one command, like this for example:

$factorialOfFive = $($fact = 5; 1..$fact | foreach {$result=1}{$result *= $_}{$result})
$factorialOfFive
120

It works but there is no added value over doing it on more lines, the $fact variable is still accessible after the subexpression is run, because no new scope was created. It is just less readable way of doing this:

$fact = 5
$factorialOfFive = 1..$fact | foreach {$result=1}{$result *= $_}{$result}
$factorialOfFive

The best way to do this achieve the same goal is to create a helper function that will take the number as input and will output the factorial, not polluting your session with unnecessary variables.

Quite often people use the subexpression when calling a cmdlet, getting property of an object or even pass string with subexpression that contains value. In most cases the task can be achieved just with parentheses. And if cannot, again, stop and think if it is necessary to do it that way. There are limitations to the commands that can be processed in parentheses that you should be aware of: It cannot contain more commands separated by ‘;’ or more that one pipeline. The following is legal because there is only one pipeline:

1..2 | %{$_} | %{$_} | %{$_}

This is not and you will recieve an error if you try to run it:

($a=1;$b=0)

By now it should be obvious that the following examples produce the same results. Some of them making your fingers tired, some not.
Don’t:

$(Get-Date).Month
$start=$(get-date).addMinutes("$(-10)")
$end = $(Get-Date)
New-TimeSpan -Start $($start) -End $($end)

Do:

(Get-Date).Month
$start=(get-date).addMinutes(-10)
$end = Get-Date
New-TimeSpan -Start $start -End $end