Filtering PowerShell Output as Text

The PowerShell command-line shell is very powerful, and for people coming from *nix backgrounds (like me), it is the closest thing in Windows to the *nix command line. Today, I wanted to find if I had installed one service in my computer, and instead of going through all the required clicks in the UI, I decided to try and do this using PowerShell.

Fetching all the services is very simple, using the Get-Service cmdlet. Now I want to filter them for specific text (as I would have done in *nix with grep). A quick Google search returned the Select-String cmdlet as the best way to do this. So I naively wrote Get-Service | Select-String "Family" (using the Family Safety service for this example). No output… What am I doing wrong?

What happened here is that PowerShell is an Object-Oriented shell, which in lay-terms means that it doesn’t treat input and output as strings, but as Objects. In this specific case, the Get-Service cmdlet return an array of System.ServiceProcess.ServiceController objects, and when they are passed to Select-String they are transformed to their type, so what Select-String is filtering is a list of elements with the text System.ServiceProcess.ServiceController (no idea why this happens, because if I store the value of Get-Service into a variable and apply toString() to it, I get the name of the service and not it’s type).

So in order to transform the output of the cmdlet to a string, we have to invoke Out-String. I executed Get-Service | Out-String | Select-String "Family" and… wrong again! Got a list of all the services in my computer. The problem now is that Out-String not only transforms the output of the previous command to a string, but if the result is an array, it will also concatenate ALL the outputs into one big string. So Select-String is not lying, I’m just giving it wrong input. Again, a quick search returned that I needed to use Out-String -stream for the output to be sent to Select-String one by one.

To conclude, the working on-liner is:

Get-Service  | Out-String -stream | Select-String "Family"

Which gives me this nice output:

Stopped  WPCSvc             Family Safety

And as it turns out, I could also have done this using a parameter to Get-Service:

Get-Service -DisplayName "Family*"

Oh… the joys of automation… XKCD is always right