How to Grep in PowerShell:
Here’s some example commands in PowerShell to replicate Grep:
- Get-Process | Where-Object {$_ | Select-String “foo”}
- get-process | where ProcessName -like “*foo*
- get-process | findstr foo
- ps *foo*
Applies To : PowerShell
For those who have lived in the Linux/Unix command line, the ‘grep‘ command is a commonly used way of finding something that you want in a chunk of data.
Øyvind Kallstad did a great writeup of comparing a bunch of ways to use PowerShell instead of grep which is worth reading.
The article covers a bunch of scenarios, and is centered around starting with the ‘grep’ command and working with it. However, there’s the other common use case of running a different command, then piping those results to grep to search for something.
This blogpost was triggered by Janet who asked me this fair question:
As with poor cute cats, there’s more than one way to skin PowerShell.
I had to do some research and asking around on this, because normally I’d filter out the property of the object I was looking at, and work with that. Using the get-process example:
get-process | where ProcessName -like "*foo*
That works, but it’s still a lot clunkier than what a grep user would expect. An easier way would be to use the ‘findstr‘ program (which also has a bunch of useful swtiches):
get-process | findstr foo
I say program because ‘findstr’ is not a PowerShell cmdlet, but it’s still native to Windows and works perfectly fine. It’s case sensitive though, so you need to use -i for case insensitive results.
That’s great for simple stuff, but we’re sort of breaking what PowerShell does. You’re no longer dealing with a standard PowerShell object, so further piping and processing won’t really work.
The ‘proper’ PowerShell way would be to use the ‘Where-Object’ command:
Get-Process | Where-Object {$_ | Select-String "foo"}
A bit longer, but you can shorten ‘Where-Object’ to ‘Where’. Although more involved, it’s good to get into the habit of doing it this way, so when you’re piping this to the next command, it still says as a standard object that can be read and manipulated.’
(Update 24th Feb 2017) As Steve_N points out in the comments section, there’s a much shorter way of doing this:
ps *foo*
That’s it. Many PowerShell commands have inbuilt aliases, including ‘get-process’. You can see what this is with the command ‘get-alias -definition get-process’
This shows that ‘gps’ and ‘ps’ are both aliases to the command ‘get-process’. You can also create your own aliases with the ‘set-alias‘ command.
The ‘*foo*’ part works because the command assumes the -name switch has been used, which lets you define what criteria to search and show in the ProcessName field. This is the same way that many commands don’t need the -identity switch used, because it’s written to assume you’re going to tell it what identity/username/upn to work with.
This can also be piped to something else, so it’s a winner. It’s less ideal for scripts though, because it’s much harder to read, and you can’t assume that everyone will know the short alias of a full command.
Also note that this isn’t grep related at all, so part of the answer to the original question is that you may not even need grep or select-string as it adds unnecessary overhead of getting data and parsing it, whereas this updated example filters the data as it’s obtained.
(Update ends)
PowerShell isn’t a Linux/Unix command line, but Microsoft have incorporated many of the concepts from bash. If you still can’t bear to use PowerShell on Windows, there’s always the Linux Bash Shell on Windows.
Thanks again to Steve Mclnerey for the grep advice :)
In Powershell
ps *foo*
would get you the result as an object with only 8 keys to press.
Another way that some may prefer is
ps|ogv
and type foo into the filter
Excellent point Steve, I’ll update the article about this!
Thanks for `ogv`, what a cool feature!
And if you want just the name of the process, not the whole object:
(ps *foo*).name
Completely correct – although often you may want the PID for a process that has multiple identical names.
With get-process you need the Out-String, so the shortest I can get is:
gps | Out-String -Stream | sls wininit
However, when calling other console apps it becomes easier:
netsh http show urlacl | sls “Reserved URL”
If you don’t want the headers you get when using ‘gps *wininit*’ you can use ‘gps *wininit* | ft -h’
A little late to this party but you still top the “grep powershell” google results, so..
I was here looking for a better way but I guess mine is as good as it gets since it seems to match all the criteria “proper” but is a little less clunky than that example, using an even shorter Where-Object alias:
” | ?{$_ -match ‘foo’}”
is my “grep foo” that keeps pipeline etc. and works the same as grep.
What does that give you over the PowerShell command
ps *foo*
Regarding performance
The ‘grep’ functionality is built into the CMDLET, it’s not best practice to have an object oriented language build an array and pass each member across a pipe to then be filtered upon when the filtering capability is built into the CMDLET.
As Adam mentioned in the update “it adds unnecessary overhead of getting data and parsing it, whereas this updated example filters the data as it’s obtained.”
This can be over 100 times the amount CPU time required and has a larger memory footprint too. I ran each command 20 times and averaged out the response times;
“gps ‘win*’” executed on average in 5.69507 milliseconds
“get-process | where ProcessName -like “*win*”” executed on average in 12.16402 milliseconds
“gps ‘win*’|ft -H” executed on average in 14.84574 milliseconds
“gps| ?{$_ -match ‘win’}” executed on average in 14.237665 milliseconds
“Get-Process | Where-Object {$_ | Select-String “win”}” executed on average in 55.802275 milliseconds
“gps| Out-String -Stream | sls win” executed on average in 559.78359 milliseconds
“get-process | findstr win” executed on average in 635.51057 milliseconds
Filtering should always be done as early as possible in your code, the CMDLET will ‘grep’ for you.