PowerShell – How to use Select-String to search files for a pattern

In this article, I’ll show how to use the Select-String PowerShell command to search files in a directory with a regex pattern. I’ll first explain the basic syntax, and then I’ll show a few common search scenarios.

Pattern, path, and alias

Select-String has two main parameters:

  • pattern: what to search for.
  • path: where to search.

Note: The command name is not case-sensitive. You can use ‘select-string’ instead of ‘Select-String’. Personally I prefer lowercase commands and parameters.

The following is an example of using the named -path and -pattern parameters with select-string:

select-string -pattern "public class \w+" -path "D:\Projects\aspdotnet-background-dblogger\*.cs"
Code language: PowerShell (powershell)

You can exclude the parameter names as long as you specify the pattern first, then the path. For example:

select-string "public class \w+" "D:\Projects\aspdotnet-background-dblogger\*.cs"
Code language: PowerShell (powershell)

Select-String’s command alias is sls. Usually when you’re typing commands on the command line, you’ll want to use the shortest possible syntax. The following is an example of using the command alias:

sls "public class \w+" "D:\Projects\aspdotnet-background-dblogger\*.cs"
Code language: PowerShell (powershell)

If you open the PowerShell terminal from the desired directory, you can exclude the path, like this:

sls "public class \w+" *.cs
Code language: PowerShell (powershell)

All four of the commands above are functionally the same. Throughout this article, I’ll use the full select-string command name and named parameter flags for clarity.

Search the top-level directory

The following command searches for the regex pattern “public class \w+” in the directory D:\Projects\aspdotnet-background-dblogger\:

select-string -pattern "public class \w+" -path "D:\Projects\aspdotnet-background-dblogger\*.*"
Code language: PowerShell (powershell)

This outputs matching lines, including context about where it was found (FileName:LineNumber):

Program.cs:12:    public class Program
Startup.cs:12:    public class StartupCode language: plaintext (plaintext)

Don’t forget the path separator (\) and a file specifier (*.*) at the end of the path

When you want to search the files in a directory, PowerShell expects the path to contain a path separator (\ or /) and a file specifier (*.*) at the end.

For example: D:\Projects\*.* is correct. D:\Projects and D:\Projects\ are incorrect.

If you exclude these characters in the path, you’ll run into unexpected behavior. In recent versions of PowerShell, it will fail silently and not search everything. In earlier versions of PowerShell, it fails with the error messages shown below.

If you don’t end the directory with a path separator (ex: D:\Projects), you get this error:

sls : The file D:\Projects cannot be read: Access to the path is denied

If you forget the *.* at the end of the path (ex: D:\Projects\), you get this error:

sls : The file D:\Projects\ cannot be read: Could not find a part of the path

Filter by file extension

You can filter by file extension. For example, to search for files with the .cs extension (C# source files), use *.cs in the path name like this:

select-string -pattern "Connection" -path "D:\Projects\aspdotnet-background-dblogger\*.cs"
Code language: PowerShell (powershell)

This outputs the following:

D:\Projects\aspdotnet-background-dblogger\Startup.cs:27:                => new
LogRepository(Configuration.GetConnectionString("Default")));

Code language: plaintext (plaintext)

Filtering by multiple file extensions

You can also filter by multiple file extensions by using the -include flag, like this:

select-string -pattern "Connection" -path "D:\Projects\aspdotnet-background-dblogger\*.*" -include *.cs,*.json
Code language: PowerShell (powershell)

This outputs the following:

D:\Projects\aspdotnet-background-dblogger\appsettings.Development.json:9:  "ConnectionStrings": {
D:\Projects\aspdotnet-background-dblogger\appsettings.Development.json:10:    "Default": "The connection string is
defined in the user secrets file"
D:\Projects\aspdotnet-background-dblogger\appsettings.json:10:  "ConnectionStrings": {
D:\Projects\aspdotnet-background-dblogger\appsettings.json:11:    "Default": "The connection string is defined in the
user secrets file"
D:\Projects\aspdotnet-background-dblogger\Startup.cs:27:                => new
LogRepository(Configuration.GetConnectionString("Default")));Code language: plaintext (plaintext)

How do you use -include and -exclude at the same time?

Let’s say you want to exclude appsettings.Development.json from the above example. In other words, you’re including JSON files, but excluding this specific file. Select-String -include and -exclude don’t work together (Note: I’m not sure if this is a bug, or if it’s by design).

Instead, use Get-ChildItem (alias ls) to filter the files and pipe them to Select-String, like this:

 ls "D:\Projects\aspdotnet-background-dblogger\*.*" -include *.cs,*.json -exclude appsettings.Development.json | select-string -pattern "Connection"
Code language: PowerShell (powershell)

This outputs the following results:

D:\Projects\aspdotnet-background-dblogger\appsettings.json:10:  "ConnectionStrings": {
D:\Projects\aspdotnet-background-dblogger\appsettings.json:11:    "Default": "The connection string is defined in the
user secrets file"
D:\Projects\aspdotnet-background-dblogger\Startup.cs:27:                => new
LogRepository(Configuration.GetConnectionString("Default")));Code language: plaintext (plaintext)

Notice that it excluded appsettings.Development.json.

Search all subfolders

By default, Select-String only searches the top-level directory.

To recursively search all subfolders, you have to use Get-ChildItem (alias ls) with the recurse flag (-r) and pipe it into Select-String, like this:

ls "D:\Projects\aspdotnet-background-dblogger\" -r | select-string -pattern "public class \w+"
Code language: PowerShell (powershell)

This outputs the following:

Program.cs:12:    public class Program
Startup.cs:12:    public class Startup
Controllers\RecipesController.cs:10:    public class RecipesController : ControllerBase
Logging\DatabaseLoggerService.cs:12:    public class DatabaseLoggerService : BackgroundService, ILoggerService
Logging\LogMessage.cs:8:    public class LogMessage
Logging\LogRepository.cs:10:    public class LogRepository : ILogRepositoryCode language: plaintext (plaintext)

Notice that it searched the subfolders (\Logging\ and \Controllers\).

Note: If you’re used to using grep, you may find it strange that Select-String doesn’t have its own recurse flag. I don’t know what the rationale is behind that design, but there are other cases where you need to use multiple commands together to get the end results you want, so it’s not all that unusual.

Search a specific file

In addition to being used to search multiple files in a directory, you can also search a specific file, like this:

select-string -pattern "public class \w+" -path "D:\Projects\aspdotnet-background-dblogger\Startup.cs"
Code language: PowerShell (powershell)

This outputs the following:

Startup.cs:12:    public class StartupCode language: plaintext (plaintext)

How to only show the matched string and not the context info

By default, Select-String doesn’t return simple strings. It returns MatchInfo objects. To see this, display the output as a list of objects using Format-List (alias fl):

 select-string -pattern "public class \w+" -path "D:\Projects\aspdotnet-background-dblogger\Startup.cs" | fl
Code language: PowerShell (powershell)

This outputs the following MatchInfo object:

IgnoreCase : True
LineNumber : 12
Line       :     public class Startup
Filename   : Startup.cs
Path       : D:\Projects\aspdotnet-background-dblogger\Startup.cs
Pattern    : public class \w+
Context    :
Matches    : {0}Code language: plaintext (plaintext)

What if you only want the matched string and not the context info about where it was found?

In PowerShell 7, the -raw parameter was added to the Select-String command for this purpose:

select-string -pattern "public class \w+" -path "D:\Projects\aspdotnet-background-dblogger\Startup.cs" -raw
Code language: PowerShell (powershell)

This outputs the matched string instead of the MatchInfo object:

public class StartupCode language: plaintext (plaintext)

In previous versions of PowerShell

If you’re using an older version of PowerShell where the -raw parameter isn’t available, and you just want to show the matched strings, then you could pipe the results to Format-Table (alias fw) with the -HideTableHeaders flag, like this:

select-string -pattern "public class \w+" -path "D:\Projects\aspdotnet-background-dblogger\Startup.cs" | select-object Line | ft -HideTableHeaders
Code language: PowerShell (powershell)

This outputs the following (showing the blank newlines on purpose):


    public class Startup                                     

Code language: plaintext (plaintext)

It’s not exactly the same as using the -raw parameter, but it’s close. The difference is -raw doesn’t include all the extra newlines.

Leave a Comment