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
|| http://gnuwin32.sourceforge.net/packages/grep.htm || findstr || go Cygwin!!!
(…yes, I know you work for Microsoft…)
Do you really think I am not aware of Cygwin? :-). But I like knowing the tools that best work in my platform, and PowerShell is VERY powerful, and you can do incredible stuff. And learning new things is always good for the brain.
Thank you, this was straight forward tip!