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
|| 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!