Prof. Powershell
Working with Values and Variables in PowerShell
One object or many, PowerShell hands you control. This week's tip is a good example.
- By Jeffery Hicks
- 10/02/2012
If you've been following me for any length of time you have probably heard me mention that PowerShell is all about the objects. Working with objects in the pipeline is what makes PowerShell such an awesome automation engine. But sometimes, we need to work with a single object and even then sometimes just a single property of that object. It can be tricky for PowerShell protégés to figure this out.
Let's say you've retrieved a service object and saved it to a variable:
PS C:\> $s=get-service bits
To display just the displayname, you can easily access it by the property name:
PS C:\> $s.DisplayName
Background Intelligent Transfer Service
A problem you can run into is when you want to include that property, say, in a message. Your first inclination might be something like this:
PS C:\> Write-Host "Analyzing $s.displayname" -foreground Green
Analyzing System.ServiceProcess.ServiceController.displayname
Hardly the result you were expecting. There are several ways you could fix this. You could create a separate variable:
PS C:\> $display=$s.DisplayName
PS C:\> Write-Host "Analyzing $display" -Foreground Green
Analyzing Background Intelligent Transfer Service
You might use the -f operator, which is a great way for really complex messages:
PS C:\> $msg="Analyzing {0}" -f $s.DisplayName
PS C:\> Write-Host $msg -Foreground Green
Analyzing Background Intelligent Transfer Service
Or you can use a sub-expression:
PS C:\> Write-Host "Analyzing $($s.displayname)" -foreground Green
Analyzing Background Intelligent Transfer Service
The sub-expression wraps the object, $s.displayname, in a set of parentheses which tells PowerShell to evaluate the expression. The $ in front of the parentheses then tells PowerShell to treat the whole thing as a variable, so that variable expansion works.
These techniques work fine for building strings based on individual object properties. Here's another variable value scenario:
PS C:\> get-process | sort Workingset | select -last 1 path
Path
----
C:\Users\Jeff\AppData\Local\Google\Chrome\Application\chrome.exe
But what I really want is to save just the path to a variable.
PS C:\> $proc=get-process | sort Workingset | select -last 1 path
PS C:\> get-item $proc
get-item : Cannot find drive. A drive with the name '@{Path=C' does not exist.
At line:1 char:9
+ get-item <<<< $proc
+ CategoryInfo : ObjectNotFound:
(@{Path=C:String) [Get-Item], DriveNotFoundException
+ FullyQualifiedErrorId :
DriveNotFound,Microsoft.PowerShell.Commands.GetItemCommand
Why didn't that work? Well, look at $proc:
PS C:\> $proc
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
131 24 111196 112544 253 444.96 2544 chrome
I actually have the full object. Knowing this, I could get by with:
PS C:\> get-item $proc.path
Directory: C:\Users\Jeff\AppData\Local\Google\Chrome\Application
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 8/29/2012 10:58 PM 1229848 chrome.exe
Or if I truly want just the path property, I can use the -ExpandProperty parameter with Select-Object. This has the effect of retrieving the value only:
PS C:\> $proc=get-process | sort Workingset | select -last 1 -ExpandProperty Path
PS C:\> $proc
C:\Users\Jeff\AppData\Local\Google\Chrome\Application
Note that you can only use ExpandProperty on a single property. But this is a great way to build an array of values from a set of object properties:
PS C:\> $procpaths=get-process | where {$_.path} | select -ExpandProperty Path -Unique
The array, $procpaths, is now a collection of strings which makes it easier to do something like this:
PS C:\> $procpaths | group {Split-Path $_} | Select -expand name | out-file c:\work\procpathparents.txt
Ideally, you will be using PowerShell cmdlets to work with objects and their properties in bulk, but should the need arise to give values and variables some special care, these are the techniques I suggest.
About the Author
Jeffery Hicks is an IT veteran with over 25 years of experience, much of it spent as an IT infrastructure consultant specializing in Microsoft server technologies with an emphasis in automation and efficiency. He is a multi-year recipient of the Microsoft MVP Award in Windows PowerShell. He works today as an independent author, trainer and consultant. Jeff has written for numerous online sites and print publications, is a contributing editor at Petri.com, and a frequent speaker at technology conferences and user groups.