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