Search notes:

PowerShell cmdLet Select-String

select-string allows to search for strings that match a substring or a regular expressions. Its usage is similar to that of grep of findstr.exe.
The objects that are piped into select-string need to be either System.Management.Automation.PSObject objects or have a ToString() method (See for example here).
Unless -raw or -quiet is specified, select-string returns objects whose type is Microsoft.PowerShell.Commands.MatchInfo.
With -quiet, it returns $true or $false (depending on whether the input-line matched the pattern).
With -raw (which was introduced with PowerShell 7), the cmdLet returns the matched strings as string objects.
By default, select-string tries to match with regular expressions. In order to match a substring (for example to improve performance), the -simpleMatch option needs to be given.
By default, PowerShell 7 highlights the matched string. To turn this off, the -noEmphasis parameter must be specified.
Prior versions don't have the built-in capabilities to hightlight matched strings.

Common usages

Find lines in current directory that contain pattern.
select-string pattern *
Search in files with given extensions only:
select-string -list pattern *.sql
select-string -list pattern *.sql, *.config
Unfortunately, select-string pattern * throws an error message if select-string pattern * is executed in a directory that contains sub-directories: select-string : The file P:\ath\to\subdirectory cannot be read: Access to the path 'P:\ath\to\subdirectory' is denied.
Therefore, it is advisable to use a pipeline that pipes the result of get-childItem into the select-string cmdLet;
get-childItem * | select-object pattern
Use -list to report only the first occurence of a found pattern;
get-childItem * | select-string -list pattern
Show filenames only. filename is a property of the returned MatchInfo objects:
select-string -list pattern * | select-object -list filename
get-childitem -recurse | select-string -list pattern * | select-object -list filename
More properties, such as lineNumber can be added to the result:
select-string -list pattern * | select-object lineNumber, filename
Search recursively in or below the current directory:
get-childItem -recurse | select-string pattern
By default, select-string searches case-insensitively. Case sensitiveness can be turned on with -caseSensitive:
get-childItem -recurse | select-string -caseSensitive pattern

Find strings in an array

The following simple pipeline pipes a string array into select-string and uses a regular expression to find all strings that don't have the letter e:
'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten' | select-string '^[^e]*$' 
Github repository about-PowerShell, path: /cmdlets/string/select/string-array.ps1
This pipeline prints:
two
four
six
Similarly, such a pipeline can also be used to find numbers that have at least two digits:
42, 128, 1, 44 | select-string \d\d

Selecting lines in files

With the -path option, select-string searches for text in files, line by line:
select-string -path .\*.txt 'regexp'
Whe using select-string on files, the output includes the name of the file and the line number where the text was found:
bar.txt:603:quod erat
foo.txt:109:demonstrandum
foo.txt:721:etc etc etc

Selecting objects with specific property values (-inputObject parameter)

The following object creates an array of psCustomObjects and then uses the -inputObject option of select-object to find the property-values of the property val_2 that contain the letter a:
$objs  = `
     ( new-object psObject -property @{ val_1 = 'one'   ; val_2 = 'two'   ; val_3 = 'three' } ) ,
     ( new-object psObject -property @{ val_1 = 'foo'   ; val_2 = 'bar'   ; val_3 = 'baz'   } ) ,
     ( new-object psObject -property @{ val_1 = 'abcde' ; val_2 = 'fghij' ; val_3 = $null   } ) ;

$objs | select-string -inputObject { $_.val_2 } -pattern 'a'
Github repository about-PowerShell, path: /cmdlets/string/select/psCustomObject.ps1
This approach also works on non-psCustomObjects. The following example pipes the result of get-winEvent into select-object to find messages that contain the word failed.
get-winEvent application | select-string -inputObject {$_.message} -pattern 'Failed'
Github repository about-PowerShell, path: /cmdlets/string/select/get-winEvent_inputObject.ps1

Displaying lines before and after matched line

The -context option takes an array of one or two integers that specify how many lines before and after a matched line should be displayed (and thus provides the functionality that grep offers with its -A, -B and -C options).
Display two lines before and two lines after the matched string:
get-childItem -recurse -include *.sql | select-string  from -context 2
Display one line before and two lines after the matched string:
get-childItem -recurse -include *.sql | select-string  from -context 1,2
In the displayed result, a leading greater than (>) indicates the line that actually matched:
  dir-one\abc.sql:4:  sal.amount
> dir-one\abc.sql:5:from
  dir-one\abc.sql:6:  sales    sal                            join
  dir-one\abc.sql:7:  customer cus on sal.cust_id = cus.id;
  dir-two\dir with spaces\def.sql:7:   t2.baz
> dir-two\dir with spaces\def.sql:8:from
  dir-two\dir with spaces\def.sql:9:   t1                    left join
  dir-two\dir with spaces\def.sql:10:   t2 on t1.id = t2.id_1;

Simulating the Linux strings functionality

The following example tries to approximately simulate the strings Linux command line command.
The task I have given myself is to extract the words with excactly 5 letters from the following file (named text):
one two three
four five six
seven eight nine
ten elevent twelve.
Github repository about-PowerShell, path: /cmdlets/string/select/strings/text
Of course, these words are three, seven and eight.
This can be achieved with the following pipeline
foreach($matchedLine in
   get-content text       |
   select-string -allMatches '\b\w{5}\b'
) {
   foreach ($matchedWord in $matchedLine.matches) {
      "$($matchedLine.lineNumber): $matchedWord"
   }
}
Github repository about-PowerShell, path: /cmdlets/string/select/strings/strings.ps1
Explanation: first, the text of the file is read with get-content and then piped into select-string.
The pattern that matches exactly 5 times is \b\w{5}\b:
select-string is given the -allMatches option because there are lines where this pattern matches multiple times and I want to capture them all.
The result of this select-string operation is processed in a foreach statement that iterates over each line that matched at least once. This line is assigned to $matchedLine.
In order to find the matched words, another foreach statement that iterates over $matchedLine.matches and assigns each match to $matchedWord.
Finally, the line number and the matched word can then be printed:
1: three
3: seven
3: eight

Recursive search for a pattern (grep -R)

The following pipeline tries to demonstrate how select-string, in conjunction with get-childItem might be used to recursively search for text patterns in files that are located in and below a given directory. Thus, it sort of does what is known in the shell universe as grep -R.
get-childItem                                        <# Get a list of files                         #> `
  -recurse                                           <# recursively                                 #> `
  ..\..                                              <# start two directories further up            #> `
  -include '*.ps1'                                   <# Only interested in PowerShell scripts       #> |
  select-string                                      <# pipe found files into select-string         #> `
 'foo.*bar' `                                        <# find files that contain foo followed by bar #> |
  format-table                                       <# Pipe to format-table for nice output        #> `
  lineNumber                                         <# We want lineNumber                          #> ,
  @{name='rel. path' ;                               <# relative path                               #> `
   expression={ resolve-path -relative $_.path }}    <#(which is returned by resolve-path …)        #> ,
   line                                              <# and the text of the matching line           #>
Github repository about-PowerShell, path: /cmdlets/string/select/grep-recursively.ps1
When executed, these cmdLets might print something like
LineNumber rel. path                      Line
---------- ---------                      ----
         1 ..\..\gridView\out\array.ps1   'foo', 'bar', 'baz' | out-gridView -title 'An array'
         4 ..\..\json\convertTo\basic.ps1   bla = 'foo', 'bar', 'baz'
         6 .\grep-recursively.ps1          'foo.*bar' `                                        <# find files that contain foo followed by bar #> |
         1 ..\..\write-host.ps1           write-host "Foo bar baz" -foregroundColor red -backgroundColor yellow
        15 ..\..\write-host.ps1           write-host "foo bar" -noNewline
        17 ..\..\write-host.ps1           # foo bar baz

Alias

An alias for select-string is sls.

See also

select-string belongs to the cmdlets with the -encoding parameter.
findstr.exe
Powershell command noun: string

Index