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 -DisplayName "Family*"
Oh… the joys of automation… XKCD is always right