/commands/powershell/select-string-grep
Select-String: the PowerShell equivalent of grep
- tool
- PowerShell
- versions
- Windows PowerShell 5.1, PowerShell 7.x
- os
- windows, linux, macos
Select-String searches text for a regular expression, like grep. Give it files with -Path or pipe strings into it; each match is a MatchInfo object that prints as file:line:text. Unlike grep, it is case-insensitive by default.
Select-String -Path *.log -Pattern 'error'
Get-Content app.log | Select-String 'timeout'
Get-ChildItem -Recurse -Filter *.ps1 | Select-String -Pattern 'Invoke-WebRequest'Examples
'error: disk full', 'ok', 'Error: timeout', 'warning: slow' | Set-Content app.log
'--- default (case-insensitive):'
Select-String -Path app.log -Pattern 'error' | ForEach-Object { "$($_.Filename):$($_.LineNumber):$($_.Line)" }
'--- -CaseSensitive:'
(Select-String -Path app.log -Pattern 'error' -CaseSensitive).Line
'--- -NotMatch (grep -v):'
(Select-String -Path app.log -Pattern 'error' -NotMatch).Line
'--- capture group:'
Select-String -Path app.log -Pattern 'error: (\w+)' | ForEach-Object { $_.Matches[0].Groups[1].Value }
'--- -SimpleMatch (no regex), -Quiet (true/false):'
Select-String -Path app.log -Pattern 'disk full' -SimpleMatch -Quiet
'--- -Context 1,0 (one line before):'
Select-String -Path app.log -Pattern 'timeout' -Context 1, 0 | ForEach-Object { $_.Context.PreContext + $_.Line }Output
--- default (case-insensitive): app.log:1:error: disk full app.log:3:Error: timeout --- -CaseSensitive: error: disk full --- -NotMatch (grep -v): ok warning: slow --- capture group: disk timeout --- -SimpleMatch (no regex), -Quiet (true/false): True --- -Context 1,0 (one line before): ok Error: timeout
grep to Select-String
| grep | Select-String |
|---|---|
| grep error file | Select-String -Pattern error -Path file -CaseSensitive |
| grep -i error file | Select-String error file (default) |
| grep -v error file | Select-String error file -NotMatch |
| grep -r error . | Get-ChildItem -Recurse -File | Select-String error |
| grep -l error *.log | (Select-String error *.log -List).Path |
| grep -c error file | (Select-String error file).Count |
| grep -F "a.b" file | Select-String "a.b" file -SimpleMatch |
| grep -o "id=[0-9]*" | Select-String 'id=\d+' -AllMatches | % { $_.Matches.Value } |
| grep -A2 -B1 error | Select-String error -Context 1,2 |
| grep -q error file | Select-String error file -Quiet |
-Pattern takes .NET regular expressions (\d, \w, lookarounds, (?i)), which are richer than findstr's. Use .Line to get the plain text of each match, since piping MatchInfo objects to other commands passes objects, not strings. PowerShell 7 adds -Raw to output only the matching strings.
When to use it
Use Select-String to search log files, configuration files and source code on Windows without installing grep, and in PowerShell scripts that need to act on the matches: count errors, extract IDs with a capture group, or fail a build when a file contains a forbidden string. It runs the same way in Windows PowerShell 5.1 and PowerShell 7 on any platform. In cmd batch files, the built-in alternative is findstr, which has a much smaller regular expression syntax.
What a match contains
| Property | Content |
|---|---|
| Line | The full text of the matching line |
| LineNumber | Line number in the file, starting at 1 |
| Path / Filename | Full path and file name; InputStream when the text came through the pipeline |
| Pattern | The pattern that matched (useful with several patterns) |
| Matches | The regex matches, with Groups for capture groups and Value for the matched text |
| Context | PreContext and PostContext lines when -Context is used |
Pitfalls
'alpha', 'beta', 'gamma' | Set-Content words.txt
'--- several patterns (OR):'
(Select-String -Path words.txt -Pattern 'alp', 'gam').Line
'--- piped text has no file name:'
Get-Content words.txt | Select-String 'beta' | ForEach-Object { "$($_.Filename):$($_.LineNumber)" }
'--- an unescaped regex character:'
try { Select-String -Path words.txt -Pattern '(' -ErrorAction Stop } catch { $_.Exception.Message }Output
--- several patterns (OR):
alpha
gamma
--- piped text has no file name:
InputStream:2
--- an unescaped regex character:
The string ( is not a valid regular expression: parsing "(" - Not enough )'s.- Regex characters.
.,(,[,$and\have a meaning in-Pattern. Searching for a literal string such as a file path or an IP address needs-SimpleMatchor[regex]::Escape($text). - Piping Get-Content. It works, but the matches lose the file name and path, so reports cannot say where a line came from.
- Recursive search.
-Pathdoes not recurse; combineGet-ChildItem -Recurse -FilewithSelect-String, and filter by extension with-Filteror-Includeto skip binary files. - One result per line. Without
-AllMatches, only the first match in each line is kept inMatches, even if the pattern appears several times.
FAQ
How do I search for several words at once?
Pass an array to -Pattern, for example -Pattern 'error','timeout'. A line matches when any of the patterns matches, like grep -e error -e timeout.
Why does the output show InputStream instead of the file name?
The text was piped from Get-Content, so Select-String never saw a file. Use -Path, or pipe Get-ChildItem output, to keep the file name and path in each match.
How do I get only the text of the matching lines?
Read the Line property, for example (Select-String error app.log).Line, or use -Raw in PowerShell 7. Without it you get MatchInfo objects that display with the file name and line number.