Windows comes with the two command-line tools find and findstr that allow you to search in text files. The PowerShell cmdlet Select-String offers many advantages over these old utilities.

Because PowerShell isn’t just a scripting language but is also an interactive shell, Select-String is a mighty alternative to find and findstr. Whereas find is a somewhat limited tool from the DOS times, findstr implements at least the most important functions of grep for Windows.

However, PowerShell goes beyond the capabilities of those tools, among others, because of a full implementation of regular expressions. In addition, it offers the common advantages of a cmdlet that is well integrated in PowerShell.

In the simplest usage scenario, you just have to pass a filename (or, with the help of wildcards, a pattern of filenames) and the search string:

Select-String -Path index.html -Pattern "Home"

This command returns all lines that contain the search string, including the line numbers. The comparison is case-insensitive, which is an advantage compared to the old tools where you always have to add the parameter /i. In PowerShell, it is just the other way around: the more uncommon case-sensitive search has to be activated explicitly with the -CaseSensitive switch.

The -Pattern parameter is reserved for the search with regular expressions. For a simple text search in a file, you can use the -SimpleMatch parameter instead:

Select-String -Path index.html -SimpleMatch "Home"

Reverse search with -NotMatch ^

With -NotMatch, PowerShell’s grep counterpart Select-String also supports reverse search, which finds only lines that don’t contain the search pattern. You may further restrict your search by excluding files with the parameter -Path, together with wildcards:

Select-String -Path *.* -Exclude *.pdf,*.zip -Pattern "DO.*=" -NotMatch

Search in subdirectories with Get-ChildItem ^

In the above example, Select-String searches only in files in the current directory, excluding ZIP archives and PDF files. In contrast to find and findstr, Select-String cannot search recursively in subdirectories. However, you can accomplish this by piping the output of the Get-ChildItem cmdlet into Select-String:

Get-ChildItem *.* -Exclude *.pdf -Recurse | Select-String -Pattern "DO.*="

Because Get-ChildItem selects the files, Select-String only has to take care of the search pattern.

Search results as MatchInfo objects ^

As opposed to text-oriented tools such as find, a cmdlet returns an object (in the case of Select-String of the type MatchInfo). Compared to flat text, objects have the advantage of offering methods and properties that allow you to process their output in a more sophisticated way. If you pass the output of Select-String to Get-Member, you’ll receive a list of all methods and properties.

Usage of MatchInfo properties ^

For instance, properties are useful for changing the output of the cmdlet if you are unhappy with the relatively confusing presentation of path, filename, and line number of Select-String. (The parameter -Context also displays lines before and after the line with the match.)

For example, the command below only displays filenames and lines that contain the search string:

Select-String -Path index.html -SimpleMatch "Home" | Select FileName, LineNumber

If you work with regular expressions, Matches is another particularly helpful property. With complex regular expressions, you are often uncertain about the strings they actually match. The example below shows the exact matches:

Select-String -Path *.* -Pattern "DO.*=" | Select Matches

By default, you only see the first match in each line. To get all matches, you have to add the -AllMatches switch.

String processing of matches ^

Because you can pass the result of Select-String to a variety of cmdlets, you have almost unlimited possibilities to process the results. One example would be to format the output in a much easier-to-read layout by piping the result into Format-List or Out-GridView. Of course, various functions exist that enable you to modify the found substrings.

The following example converts the output into lowercase letters:

Subscribe to 4sysops newsletter!

Select-String -Path *.cmd -Pattern "do.*=" | ForEach {$_.ToString().ToLower()}

You could also extract a substring or concatenate it with another string.

  1. dbutts 6 years ago

    Hi, thanks for the tips.. I wonder if what I’m attempting to do is possible WITHOUT having to output to a new file and then searching it.. here’s the scenario:

    I have a large log file (logfile.txt).

    I want to search logfile.txt for the following things: “TransmissionTimeout” and on the same line any one of a list of possible server names.. here is an example line from the log

    “Sep 30 09:33:32.910 2016@LM_DEBUG MsmqTransportUtility::SendIntegrationMessage: Sending TRANSMISSIONTIMEOUT message on MSMQ transport Server1 took 27.000 s.”

    …where Server1 is a servername.. now, I’m only interested in CERTAIN Server Names (servers.txt) and I want to ignore any server name that’s NOT in servers.txt but I want a report of any entry that includes one of the servers in servers.txt along with the value of “took secs”..

    How would I go about that?

    • Michael Pietroforte 6 years ago

      You have to iterate through your server list. I guess there are more elegant ways to do this, but this command could work:

      ForEach ($Server in Get-Content servers.txt) {Get-Content logfile.txt | Select-String $Server | Select-String “TransmissionTimeout”}

Leave a reply

Your email address will not be published.


© 4sysops 2006 - 2022


Please ask IT administration questions in the forums. Any other messages are welcome.


Log in with your credentials


Forgot your details?

Create Account