Prof. Powershell

PowerShell Objects in a Pipeline

I'm sure you've heard me stress repeatedly that PowerShell is all about the objects and specifically objects in a pipeline. This concept can take some time to get your head around. Beginners will try a command like this:

PS C:\> get-service bits | select requiredservices

RequiredServices
----------------
{RpcSs, EventSystem}

Sure, you can make sense from the output, but if all you want is to see this property you should take an extra step. First, anytime you see stuff in { } that is your clue that there are multiple objects. In PowerShell, you can expand that property when using Select-Object.

PS C:\> get-service bits | select -expandproperty requiredservices

Status   Name               DisplayName
------   ----               -----------
Running  RpcSs              Remote Procedure Call (RPC)
Running  EventSystem        COM+ Event System

The parameter name is pretty clear, isnt' it? Now we get complete objects written to the pipeline. This means you could start with a command like this:

PS C:\> get-acl \\chi-fp02\salesdata | select -expand access

The results can be seen here:

By the way, even though the window title shows PowerShell 4 what I'm explaining applies to all versions of PowerShell.

In the example, I can pipe the expanded objects to something else.

PS C:\> get-acl \\chi-fp02\salesdata | select -expand access | where {$_.FileSystemRights -match "full"} | select IdentityReference

IdentityReference
-----------------
NT AUTHORITY\SYSTEM
BUILTIN\Administrators

I filtered the ACL object and then selected just the IdentityReference property to identify who has full control in the SalesData share. Here's another way to use -ExpandProperty.

I see people run a command like this:

PS C:\> get-service | where {$_.status -eq 'running'} | select Name | out-file c:\work\running.txt

What they are expecting is a text file with only the name of running services. Instead they will see this in the text file.

Name
----
Appinfo
AudioEndpointBuilder
Audiosrv
BcmBtRSupport

What they have is a collection of objects with a single property, Name, saved to the text file. What they really want is this:

PS C:\> get-service | where {$_.status -eq 'running'} | select -expand Name
Appinfo
AudioEndpointBuilder
Audiosrv
BcmBtRSupport
BFE

I skipped sending the output to a file so you could see the difference. Notice there is no "Name" heading. Just a list of service names. In PowerShell 3 and later you could also do this:

PS C:\> (get-service | where {$_.status -eq 'running'}).Name
Appinfo
AudioEndpointBuilder
Audiosrv
BcmBtRSupport
BFE

The expression within the () is evaluated and returned as a collection of objects, and all I am doing is asking for the Name property.

Before you get too excited, know that you can only expand one property at a time. This command will throw an exception.

PS C:\> get-service bits | select -expand ServicesDependedOn,RequiredServices

You also can't really expand a property and select a property, at least not for the incoming object. A command like this will work:

PS C:\> get-process powershell | select –property Id -expand modules

   Size(K) ModuleName                                         FileName
------- ----------                                         --------
484 powershell.exe                                     C:\Windows\Sys...
1700 ntdll.dll                                          C:\WINDOWS\SYS...
1252 KERNEL32.DLL                                       C:\WINDOWS\sys...
1080 KERNELBASE.dll                                     C:\WINDOWS\sys...
660 ADVAPI32.dll                                       C:\WINDOWS\sys...

I'm not getting the ID property of the PowerShell process. Sometimes you might even get an exception. If you use this syntax you have to remember, that the property is from the expanded object. Although I am more likely to do something like this:

PS C:\> get-process powershell | select -expand threads | Sort ThreadState | Select ID,StartTime,ThreadState,WaitReason

Id StartTime                   ThreadState WaitReason
-- ---------                   ----------- ----------
6532 1/21/2014 9:26:2...             Running
6952 1/21/2014 11:51:...                Wait EventPairLow
3972 1/21/2014 9:34:5...                Wait UserRequest
3848 1/21/2014 11:51:...                Wait ExecutionDelay
7252 1/21/2014 12:00:...                Wait EventPairLow
6584 1/21/2014 11:52:...                Wait UserRequest
6560 1/21/2014 9:26:2...                Wait EventPairLow
6536 1/21/2014 9:26:2...                Wait UserRequest
1516 1/21/2014 9:26:2...                Wait UserRequest
6528 1/21/2014 9:26:2...                Wait UserRequest
2856 1/21/2014 9:26:2...                Wait UserRequest
6168 1/21/2014 9:26:2...                Wait ExecutionDelay

I've avoided any errors about duplicate property names and I think the pipeline is a bit easier to understand.

So the next time you aren't getting results you are expecting, check and see if you need to have PowerShell expand something for you.

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.

comments powered by Disqus
Most   Popular