Creating Select-String Tools
In the previous article, I demonstrated how to take advantage of the Select-String
cmdlet. This is the PowerShell version of grep. However, that doesn't mean you have to use it. I am an advocate of the right tool for the job. You have to determine what you are trying to accomplish. Do you want to do something else with the results in PowerShell? Do you merely need a report? How many files are you searching and is performance a concern?
A reader sent me an email recommending a command-line grep replacement called ripgrep. This is a cross-platform tool that is supposedly very fast. You can install it in Windows using Winget
winget install BurntSushi.ripgrep.MSVC
Or Chocolatey
choco install ripgrep
It was already installed in my WSL Ubuntu instance.

The tool has many more features than Select-String
. My intention isn't to teach you about ripgrep
but rather to re-enforce the idea of the right tool for the job. I think a major part of that decision is knowing what you want to do with the results or what you want to accomplish.
You can learn more at the project's GitHub repository.
For now, let's return to Select-String
and see how we can create some tools around it.
We already know that command isn't that difficult to invoke.
dir *.md | Select-String 'invoke-c\w+'

The output is where things can get confusing. Select-String
writes a structured object to the pipeline. It is easy to forget about objects when parsing text. Of course, the best way to learn more is to use the Get-Member
cmdlet.
PS D:\PSBehind\manuscript> dir *.md | Select-String 'invoke-c\w+' | Get-Member
TypeName: Microsoft.PowerShell.Commands.MatchInfo
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
RelativePath Method string RelativePath(string directory)
ToEmphasizedString Method string ToEmphasizedString(string directory)
ToString Method string ToString(), string ToString(string directory)
Context Property Microsoft.PowerShell.Commands.MatchInfoContext Context {get;set;}
Filename Property string Filename {get;}
IgnoreCase Property bool IgnoreCase {get;set;}
Line Property string Line {get;set;}
LineNumber Property ulong LineNumber {get;set;}
Matches Property System.Text.RegularExpressions.Match[] Matches {get;set;}
Path Property string Path {get;set;}
Pattern Property string Pattern {get;set;}
Then I like to look at a sample object.
PS D:\PSBehind\manuscript> dir *.md | Select-String 'invoke-c\w+' | Select-Object -First 1 -Property *
IgnoreCase : True
LineNumber : 457
Line : $os = Invoke-Command -ScriptBlock {
Filename : data-files-as-script-files.md
Path : D:\PSBehind\manuscript\data-files-as-script-files.md
Pattern : invoke-c\w+
Context :
Matches : {0}
Now that I know the properties and values, I can write a PowerShell command to present the information in a more readable format. I can use the Format-Table
cmdlet to display the information in a table format.
dir *.md | Select-String 'invoke-c\w+' |
Format-Table -GroupBy FileName -Property LineNumber,@{Name="Line";Expression={$_.Line.Trim()}}

I am trimming white spaces from the matching line to make a nicer looking format. Or maybe I need to see how many matches are in each file. PowerShell makes it easy to manipulate the output and create something meaningful.
PS D:\PSBehind\manuscript> dir *.md | Select-String 'invoke-c\w+'|
Group-Object Path -NoElement | Select-Object @{Name="Path";Expression={$_.name}},Count
Path Count
---- -----
D:\PSBehind\manuscript\data-files-as-script-files.md 5
D:\PSBehind\manuscript\extending-type.md 2
D:\PSBehind\manuscript\profiles.md 5
D:\PSBehind\manuscript\writing-better-powershell-code.md 4
Or maybe I want to copy the matching files.
PS D:\PSBehind\manuscript> Select-String -pattern 'invoke-c\w+' -path *.md -List |
Copy-Item -Destination d:\temp -PassThru
Directory: D:\temp
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 3/19/2025 6:05 PM 21450 data-files-as-script-files.md
-a--- 3/19/2025 6:05 PM 41772 extending-type.md
-a--- 3/19/2025 6:05 PM 30503 profiles.md
-a--- 2/28/2025 3:23 PM 23645 writing-better-powershell-code.md
Discovering objects makes all of this possible with very little effort.