9.5. Expression Evaluation

Expressions are syntactical instructions that must be evaluated to a value. Expressions can be anything from a simple variable assignment to a more complex command, such as executing a cmdlet.

Programming languages are made up of rules for how different types of expressions are evaluated. In general, expressions are evaluated from left to right. But depending on the operations used in an expression there may be more specific rules that cause it to be evaluated differently.

In the following sections, we will explore two operators (symbols with special meaning) that are integral to working with PowerShell.

9.5.1. Grouping Expression Operator

Understanding how grouping expressions works is best discovered through an example. Let’s consider the following arithmetic (math) expression.

Arithmetic expressions are evaluated from left to right, unless some math operators (like + and *) are used. Arithmetic expressions are evaluated following the mathematical rules defined in the PEMDAS order of operations.

Windows/PowerShell
> 5 + 5
10

On its own, this is an expression (a set of instructions), not a value. It must first be evaluated (interpreted and computed) to its value of 10.

In the following example, we want to add 5 + 5 and multiply the resulting value by 2 to make 20. The overall expression contains two expressions that need to be evaluated—addition and multiplication.

Would this expression evaluate to our goal of 20?:

Windows/PowerShell
> 5 + 5 * 2
15

Expressions are evaluated with rules that depend on their context. For arithmetic expressions, the PEMDAS rules cause 5 * 2 to be evaluated first with the resulting value of 10 then being added to 5.

However, we can control the order of evaluation by grouping expressions that we want to be evaluated first. In PowerShell the grouping expression operator is a pair of parenthesis that wrap around any PowerShell expression. Just like in math, grouped expressions are evaluated from the innermost group to the outermost (overall) expression.

If we were to group the addition expression, it would be evaluated first then its resulting value would be multiplied by 2:

Windows/PowerShell
> (5 + 5) * 2
20

We can see that (5 + 5) was evaluated first, then the group was replaced with the resulting value of 10 before continuing evaluating the overall expression.

Consider a more complex example with nested groups (a grouped expression inside another):

Windows/PowerShell
> ((10 + 10) * 2) + 5

The outermost (overall) expression would be evaluated in the following steps. From the inside out:

  1. Innermost grouping: (10 + 10), a resulting value of 20
  2. Moving outwards to the next grouping: the resulting value (20) is substituted and evaluated to ((20) * 2)) = 40
  3. Outermost level: once again the grouping’s value (40) is substituted for the final evaluation (40) + 5 = 45

The same principle applies to any PowerShell expression within the grouping operators. But PowerShell expressions go well beyond basic arithmetic! Instead of evaluating to just numeric values, what is substituted for each grouped expression can be a result object of any type.

Consider a simple scenario with string objects rather than numbers. Our goal is to combine (concatenate) two strings together and determine the length of the new string that is formed. Without grouping this would be our result:

Windows/PowerShell
> "hello" + "world".length
hello5

The string "hello" is concatenated with the result of "world".length into the unexpected string "hello5". PowerShell tries to evaluate from left to right but can’t combine a string ("hello") with an expression ("world".length). It first evaluates the length to a value of 5 and then evaluates "hello" + 5".

We can use grouping to enforce "hello" + "world" being evaluated first and then checking the length property of the resulting string object:

Windows/PowerShell
> ("hello" + "world").length
10

In other words, the evaluation and substitution looked like this:

Windows/PowerShell
> (string object).length

Grouping expressions allows you to evaluate the group and then treat the group, on its closing ), as an object. The object can then be used to access properties and methods directly from the group using dot notation. The group is first evaluated to its resulting object then dot notation access is evaluated.

9.5.2. Subexpression Operator

Recall that in Bash we used the command substitution syntax $(command) to execute in-line commands. In PowerShell the same syntax is used but is referred to as a subexpression operator. At first glance the subexpression operator looks similar to the grouping operator that we just learned about.

Let’s compare the purposes of each of these operators:

  • Grouping operator: lets you control the order of evaluation in an expression
  • Subexpression operator: lets you control the execution of an expression within another

Let’s see the difference in action by trying to print the length of the combined strings inside another string expression.

First, using a grouping operator:

Windows/PowerShell
# the Bash 'echo' command can be used as an alias for Write-Output
> Write-Output "The length of the concatenated strings is: (("hello" + "world").length)"

The length of the concatenated strings is: ((
hello + world).length)

Notice that PowerShell printed the literal text inside the grouped expression rather than executing it. The quotes were in turn interpreted as literal quote characters leading to some unexpected line breaks. In other words, the grouped expression did not get evaluated.

Why wasn’t it evaluated? Because evaluation only takes place during execution. From PowerShell’s perspective, there was a single expression to be executed, the string expression.

In order for us to execute the length-calculating expression within the string expression, we can use a subexpression:

Let’s try using a subexpression instead:

Windows/PowerShell
> Write-Output "The length of the concatenated strings is: $(("hello" + "world").length)"
The length of the concatenated strings is: 10

This time the subexpression ("hello" + "world").length is executed, evaluated to 10, and its resulting value is substituted into the string expression. The string expression is then itself evaluated to become the final string printed by Write-Output.

We will see more examples of subexpressions and grouped expressions later in this course. They are valuable tools to understand for writing one-line commands in the shell. However, they are even more useful when employed in pipelines and scripts.

Tip

Use grouping expressions when you want to control the order of evaluation (from the inside out).

Use subexpressions when you need to execute an expression inside of another. In addition, only subexpressions allow you to:

  • Execute multiple commands as a single unit
  • Use keywords like for (for loops) and if (for conditional logic)

For more information about the available operators, you can visit the PowerShell operators documentation.