Posey's Tips & Tricks

How To Take Advantage of PowerShell Subexpressions

Here's how you can master subexpressions to enhance your scripting efficiency.

Have you ever noticed that if you include a variable within a PowerShell Write-Host statement, that variable is evaluated, even if you include the reference to the variable within the quotation marks? Suppose for a moment, that you were to type these commands:

$A=3
Write-Host "The value of A is "$A

Those commands would produce a line of text that says, "The value of A is 3." However, PowerShell would produce exactly the same output if you were to include the reference to $A within the quotation marks like this:

Write-Host "The value of A is $A"

You can see both outputs in Figure 1.

[Click on image for larger view.] Figure 1. PowerShell doesn't care where you place the $A variable reference.

Now here is where things get tricky. Take exactly the same commands that I just showed you and replace the quotation marks with apostrophes. This time, PowerShell tells you that the value of A is $A. You can see what this looks like in Figure 2.

[Click on image for larger view.] Figure 2. PowerShell does not evaluate the variable when you use apostrophes.

So as you can see, whenever you include text inside of apostrophes, PowerShell interprets it as literal text, whereas including that same text within quotation marks causes PowerShell to evaluate any variables that are included. This means that PowerShell will display the variable's value as opposed to just displaying the variable's name.

As interesting as this distinction might be, you may be wondering why it matters. The reason for this is that if you include a string within quotation marks, then PowerShell will try treat any text proceeded by a dollar sign as an expression, and will therefore try to evaluate that expression.

You have already seen how this works for variables, but what if you wanted to evaluate some other type of expression? To show you what I mean, let's pretend that we want to add 12345 to 67890. Typing Write-Host 12345+67890 would produce an answer. In contrast, typing Write-Host "12345+67890" would display the original string "12345+67890."

But what if we wanted to mix the two and display the problem (12345+67890) and the answer to that problem? There are a few different ways that we could accomplish this, but one of the easiest options is to take advantage of subexpressions.

Remember what I said earlier about that any time you enclose a PowerShell string in quotation marks (as opposed to apostrophes) then PowerShell will attempt to evaluate any portion of the string that includes a dollar sign. In this case however, there are no variables, so there are no dollar signs.

We could map the mathematical expression to a variable, which would then give us a dollar sign that we could use. However, there is another way to get the job done without having to resort to using variables.
The trick is to enter a dollar sign and then enclose the mathematical expression in parentheses, just after the dollar sign. This causes PowerShell to treat whatever is in parenthesis as a subexpression, which means that PowerShell will evaluate it. So let's take a look at what this looks like in practice.

Suppose for a moment that you wanted PowerShell to display 12345+67890=80235. The command for doing so would look like this:

Write-Host "12345+67890= $(12345+67890)"
[Click on image for larger view.] Figure 3. PowerShell has evaluated the subexpression within the text string.

One thing that I want to be sure to point out before I move on is that the dollar sign is only half of what's needed for a subexpression. The other element is the parenthesis. PowerShell is designed to execute whatever is inside of the parenthesis. As I will show you in a moment, this means that you can actually embed executable code into a Write-Host statement by using a subexpression.

Another important thing that you need to know is that whatever code is executed within parenthesis, the result is treated similarly to a variable, meaning that you can act on it. For example, you may have occasionally seen operations like this one:

(Get-ChildItem).Count

This command causes PowerShell to display the number of files within the current folder, but think about what is really happening. The Get-ChildItem cmdlet (which normally displays a folder's contents) is being executed inside of parenthesis. As such, we aren't seeing the results of the Get-ChildItem cmdlet (the folder's contents are not being listed), but rather it is as though the folder contents have been written to a variable, even though no variable has actually been declared. We can then use the .Count property on the Get-ChildItem cmdlet just as we might use the .Count property on a variable that contains an object.

So now that I have shown you how you can use a subexpression to do math, and explained how a subexpression executes code and then treats the output somewhat similarly to a variable, you may be wondering what else subexpressions can do. As a general rule, you can think of a subexpression as a substitute for the Invoke-Expression cmdlet. This means that with some exceptions, any command that you can execute using Invoke-Expression can also be executed as a subexpression. To put it another way, you can embed PowerShell commands within a Write-Host statement (though subexpressions can be used outside of a write-host statement, as you have already seen).

Imagine for a moment that you wanted to check the status of the WinRM service on your system. You could type something like:

Write-Host "The WinRM service is $(Get-Service WinRM  | Select-Object Status)"

Notice that I didn't just use a single command, but rather I performed an entire pipeline operation within a write-host statement, simply by using a subexpression. Admittedly, such an operation is not exactly a coding best practice, but it does illustrate the power and flexibility of subexpressions. A better way to perform the task of checking on the WinRM service would be to do something like this:

$A=Get-Service WinRM
Write-Host "The WinRM Service is $($A.Status)"

Notice that because I was referencing a specific property of an object stored within a variable (as opposed to just referencing a variable as a whole), I had to use a subexpression. This concept is discussed in more depth here.

You can see both methods illustrated in Figure 4.

[Click on image for larger view.] Figure 4. You can use a subexpression to execute PowerShell code or to examine an object property.

About the Author

Brien Posey is a 22-time Microsoft MVP with decades of IT experience. As a freelance writer, Posey has written thousands of articles and contributed to several dozen books on a wide variety of IT topics. Prior to going freelance, Posey was a CIO for a national chain of hospitals and health care facilities. He has also served as a network administrator for some of the country's largest insurance companies and for the Department of Defense at Fort Knox. In addition to his continued work in IT, Posey has spent the last several years actively training as a commercial scientist-astronaut candidate in preparation to fly on a mission to study polar mesospheric clouds from space. You can follow his spaceflight training on his Web site.

Featured

comments powered by Disqus

Subscribe on YouTube