In Powershell, how do I list all files in a directory (recursively) that contain text that matches a given regex? The files in question contain really long lines of incomprehensible text, so I don't want to see the matching line -- just the filename.

4 Answers 4


You can use Select-String to search for text inside files, and Select-Object to return specific properties for each match. Something like this:

Get-ChildItem -Recurse *.* | Select-String -Pattern "foobar" | Select-Object -Unique Path

Or a shorter version, using aliases:

dir -recurse *.* | sls -pattern "foobar" | select -unique path

If you want just the filenames, not full paths, replace Path with Filename.


  1. Get-ChildItem-Recurse *.* returns all files in the current directory and all its subdirectories.

  2. Select-String-Pattern "foobar" searches those files for the given pattern "foobar".

  3. Select-Object-Unique Path returns only the file path for each match; the -Unique parameter eliminates duplicates.

  • select -Unique... cool, learned something new. That works perfectly, thanks! Apr 15, 2014 at 18:55
  • Is . really needed? Get-ChildItem -Recurse works exactly the same I think. Jul 4, 2014 at 11:14
  • 1
    or even more concisely, gci -r | sls "foobar" | select -unique path Jun 27, 2015 at 15:11
  • If there's no matches PowerShell seems to "hang", it won't return. How does one know when the search is done if there are no matches? Apr 11, 2019 at 15:56

Note, that in powershell v1.0 and v2.0 you need to specify first position parameter (path) to work with -Recursion

technet documentation


Gets the items in the specified locations and in all child items of the locations.

In Windows PowerShell 2.0 and earlier versions of Windows PowerShell, the Recurse parameter works only when the value of the Path parameter is a container that has child items, such as C:\Windows or C:\Windows*, and not when it is an item does not have child items, such as C:\Windows*.exe.


Use the below command inside the directory you would like to perform the "grep" and change [SEARCH_PATTERN] to match what you would like to match. It's recursive, searching through all files in the directory.

dir -Recurse | Select-String - pattern [SEARCH_PATTERN]



Select-String has a -List parameter for this purpose:

Return only the first match in each input file. By default, Select-String returns a MatchInfo object for each match found.


You can use it like this:

gci -Recurse | sls -List FOOBAR

Here's what some sample results look like (searching the Windows SDK for ERROR_SUCCESS):

shared\bthdef.h:576:#define BTH_ERROR(_btStatus)   ((_btStatus) != BTH_ERROR_SUCCESS)
shared\netioapi.h:2254:    ERROR_SUCCESS on success.  WIN32 error code on error.
shared\rpcnterr.h:34:#define RPC_S_OK                          ERROR_SUCCESS
shared\winerror.h:214:// MessageId: ERROR_SUCCESS
um\advpub.h:40://      ERROR_SUCCESS_REBOOT_REQUIRED        Reboot required.
um\bluetoothapis.h:243://      ERROR_SUCCESS
um\ClusApi.h:571:_Success_(return == ERROR_SUCCESS)
um\dsparse.h:102:_Success_(return == ERROR_SUCCESS)
um\eapmethodpeerapis.h:228:// If the function succeeds, it returns ERROR_SUCCESS. Otherwise, it is
um\eappapis.h:56:// If the functions succeed, they return ERROR_SUCCESS. Otherwise, it is
um\MapiUnicodeHelp.h:583:                if ((hkeyPolicy && RegQueryValueExW(hkeyPolicy, szName, 0, &dwType, (LPBYTE)
&dwLcid, &dwSize) == ERROR_SUCCESS && dwType == REG_DWORD) ||
um\Mddefw.h:127:            routine will return ERROR_SUCCESS and the inherited data even if
um\Msi.h:1693:// Returns ERROR_SUCCESS if file is a package.
um\MsiQuery.h:192:// Returns ERROR_SUCCESS if successful, and the view handle is returned,
um\msports.h:46:    ERROR_SUCCESS if the dialog was shown
um\ncryptprotect.h:164:    ERROR_SUCCESS
um\NTMSAPI.h:1761:_Success_ (return == ERROR_SUCCESS)
um\oemupgex.h:108://  Returns:    ERROR_SUCCESS in case of success, win32 error otherwise
um\PatchWiz.h:90://                     ERROR_SUCCESS, plus ERROR_PCW_* that are listed in constants.h.
um\Pdh.h:415:_Success_(return == ERROR_SUCCESS)

If you wanted to get back the actual FileInfo objects (instead of the relative Path and a single match result), you might use it like this:

Get-ChildItem -Recurse -File | where { Select-String -Path $_ -List -Pattern FOOBAR }
  • How can I get an output like the one you show, but that list all the lines (and not just one per file) that match the pattern
    – Ponce
    Jun 1, 2023 at 19:26
  • A way too elaborate formatted version: Get-ChildItem -Recurse *.* | Select-String -List "$args" | Format-Table -HideTableHeaders -AutoSize -Property Filename, @{Expression={$_.LineNumber}; Label="LN"; Width=3}, Line. Feb 16 at 18:49

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .