39
votes

I am using the below to recursively list all files in a folder that contains the $pattern

Get-ChildItem $targetDir -recurse | Select-String -pattern "$pattern" | group path | select name

But it seems it both list files having the $pattern in its name and in its content, e.g. when I run the above where $pattern="SAMPLE" I get:

C:\tmp\config.include
C:\tmp\README.md
C:\tmp\specs\SAMPLE.data.nuspec
C:\tmp\specs\SAMPLE.Connection.nuspec

Now:

C:\tmp\config.include
C:\tmp\README.md

indeed contains the SAMPLE keywords/text but I don't care about that, I only need the command to list file names not file with content matching the pattern. What am I missing?

Based on the below answers I have also tried:

$targetDir="C:\tmp\"
Get-ChildItem $targetDir -recurse | where {$_.name -like "SAMPLE"} | group path | select name

and:

$targetDir="C:\tmp\"
Get-ChildItem $targetDir -recurse | where {$_.name -like "SAMPLE"} | select name

but it does not return any results.

6
My comment on the answer you took your edit from explains why that does not work.Matt
You latest edit would only return files named literally "Sample". Use wildcards "*sample*"Matt
You are right works when I do the pattern with "sample"u123

6 Answers

49
votes

Select-String is doing what you told it to. Emphasis mine.

The Select-String cmdlet searches for text and text patterns in input strings and files.

So if you are just looking to match with file names just use -Filter of Get-ChildItem or post process with Where-Object

Get-ChildItem -Path $path -Recurse -Filter "*sample*"

That should return all files and folders that have sample in their name. If you just wanted files or directories you would be able to use the switches -File or -Directory to return those specific object types.

If your pattern is more complicated than a simple word then you might need to use Where-Object like in Itchydon's answer with something like -match giving you access to regex.


The grouping logic in your code should be redundant since you are returning single files that all have unique paths. Therefore I have not included that here. If you just want the paths then you can pipe into Select-Object -Expand FullName or just (Get-ChildItem -Path $path -Recurse -Filter "*sample*").Fullname

12
votes
get-ChildItem $targetDir -recurse | where {$_.name -like $pattern} | select name
10
votes

To complement Matt's helpful answer:

Specifically, because what you're piping to Select-String are [System.IO.FileInfo] objects - which is what Get-ChildItem outputs - rather than strings, it is the contents of the files represented by these objects is being searched.

Assuming that you need to match only the file name part of each file's path and that your pattern can be expressed as a wildcard expression, you do not need Select-String at all and can instead use Get-ChildItem with -Filter, as in Matt's answer, or the slower, but slightly more powerful -Include.

Caveat:

  • Select-String -Pattern accepts a regular expression (e.g., .*sample.*; see Get-Help about_Regular_Expressions),

  • whereas Get-ChildItem -Filter/-Include accepts a wildcard expression (e.g., *sample*; see Get-Help about_Wildcards) - they are different things.

On a side note: If your intent is to match files only, you can tell Get-ChildItem to restrict output to files (as opposed to potentially also directories) using -File (analogously, you can limit output to directories with -Directory).


Group-Object path (group path) will not work as intended, because the .Path property of the match-information objects output by Select-String contains the full filename, so you'd be putting each file in its own group - essentially, a no-op.

When using just Get-ChildItem, the equivalent property name would be .FullName, but what you're looking for is to group by parent path (the containing directory's path), .DirectoryName), I presume, therefore:

... | Group-Object DirectoryName | Select-Object Name

This outputs the full path of each directory that contains at least 1 file with a matching file name.
(Note that the Name in Select-Object Name refers to the .Name property of the group objects returned by Group-Object, which in this case is the value of the .DirectoryName property on the input objects.)

2
votes

I went through the answer by @Itchydon

but couldn't follow the use of '-like' $pattern.

I was trying to list files having 32characters(letters and numbers) in the filename.

PS C:> Get-ChildItem C:\Users\ -Recurse | where {$_.name -match "[a-zA-Z0-9]{32}"} | select name

or

PS C:> Get-ChildItem C:\Users\010M\Documents\WindowsPowerShell -Recurse | Where-Object {$_.name -match "[A-Z0-9]{32}"} | select name

So, in this case it doesn't matter whether you use where or where-object.

0
votes

You can use select-string directly to search for files matching a certain string, yes, this will return the filename:count:content ... etc, but, internally these have names that you can chose or omit, the one you need is the "filename" to do this pipe this into "select-object" choosing the "FileName" from the output.

So, to select all *.MSG files that has the pattern of "Subject: Webservices restarted", you can do the following:

Select-String -Path .*.MSG -Pattern 'Subject: WebServices Restarted' -List | select-object Filename

Also, to remove these files on the fly, you could pip into a ForEach statement with the RM command as follows:

Select-String -Path .*.MSG -Pattern 'Subject: WebServices Restarted' -List | select-object Filename | foreach { rm $_.FileName }

I tried this myself, works 100%.

I hope this helps

0
votes

To complement the excellent answer by @mklement0, you can ask Powershell to print the full path by appending a pipe as follows:

Get-ChildItem -Recurse -ErrorAction SilentlyContinue -Force -Filter "*sample*" | %{$_.FullName}

Note: When searching folders where you might get an error based on security, hence we use the SilentlyContinue option.