432

I have started learning powershell a couple of days ago, and I couldn't find anything on google that does what I need so please bear with my question.

I have been asked to replace some text strings into multiple files. I do not necessarily know the extension of the possible target files and I don't know their location either. So far I have managed to recursively browse into the directory (get-ChildItem -recurse) and find the string I was looking for with get-content and select-string:

Get-ChildItem -recurse | Get-Content | Select-String -pattern "dummy"

The problem is, I can see the occurences of the text I am looking for, but I don't know how to tell PS to return the path and the name for every matching files as well.

How can I get the name and location of the files that contains the expression I am looking for?

Bluz
  • 5,980
  • 11
  • 32
  • 40

12 Answers12

690

This should give the location of the files that contain your pattern:

Get-ChildItem -Recurse | Select-String "dummy" -List | Select Path
Jacob Bundgaard
  • 943
  • 11
  • 29
jon Z
  • 15,838
  • 1
  • 33
  • 35
  • 3
    What if you want to also MOVE those files?... I'm getting an error with this where I can't join a | Move-Item to the end of that. – rud3y Sep 05 '12 at 12:57
  • 6
    Move-Item doesn't have `name` parameter. Try adding `| %{Move-Item $_.name }` – jon Z Sep 05 '12 at 16:48
  • 57
    `Get-ChildItem -Recurse | Select-String "dummy" -List | Select Path` returns only the first match for each file so may be a little more efficient and avoids the need to group them – ben Apr 16 '15 at 15:27
  • 15
    it's worth noticing that you can filter for only a certain file type, say `txt` files, if you use `Get-ChildItem -Recurse -Filter *.txt` instead – Girardi Apr 20 '18 at 15:47
  • 2
    @rud3y I highly suggest you write it out in a script using a `foreach` loop if you're doing large operations like that. It becomes very convoluted when you try to do all of that on one line and it is very easy to make a large mistake. I speak from experience – Kellen Stuart Sep 06 '18 at 15:44
  • You can add ... Select -Unique Path if you only want each path once. Great answer by the way. – bjorsig Feb 19 '20 at 16:11
  • what if i need to know all the occurences ? – N.K Feb 27 '20 at 02:44
  • Adding `-Force` to Get-ChildItem also includes hidden files. – David May 08 '20 at 11:05
  • 1
    This is much slower than the solution provided in @js2010's answer. I'd recommend that one over this one, especially if you are working with many files and/or have to run the command many times. – sfarbota Aug 29 '20 at 18:44
  • How can you get Path and line number together here? – Jordan May 24 '21 at 16:43
  • I encountered an issue when trying to use `Remove-Item` after matching the items. PowerShell may report the item can't be removed "because it is being used by another process". The solution was to assign matching items to a variable and then process the filtered items a to remove. – Coding101 Dec 28 '22 at 19:55
  • In case the paths outputted are cropped (this may happen if the paths found are long), just add `| Format-Table -AutoSize` at the end of the above command – naian Jan 16 '23 at 12:38
  • If you need to get and object and not only a path (as a string) it is possible to replace ` | Select Path ` by ` | Get-Item ` . This should gives : ` Get-ChildItem -Recurse | Select-String "dummy" -List | Get-Item ` – Pierre-Gilles Levallois Mar 08 '23 at 09:57
107

There are a variety of accurate answers here, but here is the most concise code for several different variations. For each variation, the top line shows the full syntax and the bottom shows terse syntax.

Item (2) is a more concise form of the answers from Jon Z and manojlds, while item (1) is equivalent to the answers from vikas368 and buygrush.

  1. List FileInfo objects for all files containing pattern:

    Get-ChildItem -Recurse filespec | Where-Object { Select-String pattern $_ -Quiet }
    ls -r filespec | ? { sls pattern $_ -q }
    
  2. List file names for all files containing pattern:

    Get-ChildItem -Recurse filespec | Select-String pattern | Select-Object -Unique Path
    ls -r filespec | sls pattern | select -u Path
    
  3. List FileInfo objects for all files not containing pattern:

    Get-ChildItem -Recurse filespec | Where-Object { !(Select-String pattern $_ -Quiet) }
    ls -r filespec | ? { !(sls pattern $_ -q) }
    
  4. List file names for all files not containing pattern:

    (Get-ChildItem -Recurse filespec | Where-Object { !(Select-String pattern $_ -Quiet) }).FullName
    (ls -r filespec | ? { !(sls pattern $_ -q) }).FullName
    
KyleMit
  • 30,350
  • 66
  • 462
  • 664
Michael Sorens
  • 35,361
  • 26
  • 116
  • 172
  • 1
    Also, if you are just looking for Files that contain the pattern anywhere, you can give up after finding the first instance by using the `-List` parameter of [`Select-String`](https://technet.microsoft.com/en-us/library/hh849903.aspx) – KyleMit Apr 17 '15 at 13:54
  • 2
    I wish the OP had asked a slight variation of the question so that this would be the AA. #1 was very useful and it's obvious that @Michael Sorens groks PS! – cBlaine Jul 22 '15 at 20:53
36

This is how I would do it, you don't need get-content:

ls -r | Select-String dummy | select line,path

or

ls -r | Select-String dummy | fl *

To see what the different properties are...

This is faster. The second argument is -filter:

ls -r . *.bat | select-string netsh

ls -r -path . -filter *.bat | select-string netsh
js2010
  • 23,033
  • 6
  • 64
  • 66
  • 3
    +1, This works perfectly for my case, however use `select Pattern, LineNumber, Filename` for more concise output. Line returns EVERYTHING on the line containing your pattern string. You can also easily output this to a csv if you'd wish. – Protonova Jul 07 '17 at 16:51
  • 3
    This should be the selected answer. It was about 4-5 times faster in my case. That makes a big difference when you have to repeat the command 600 times over many files. – sfarbota Aug 29 '20 at 18:32
  • `select Path, LineNumber, Line` is more concise. Pattern is redundant since you already know what it is – 9 Guy May 27 '22 at 16:50
25

This will display the path, filename and the content line it found that matched the pattern.

Get-ChildItem -Path d:\applications\*config -recurse |  Select-String -Pattern "dummy" 
josliber
  • 43,891
  • 12
  • 98
  • 133
user5000502
  • 251
  • 3
  • 2
18

Pipe the content of your

Get-ChildItem -recurse | Get-Content | Select-String -pattern "dummy"

to fl *

You will see that the path is already being returned as a property of the objects.

IF you want just the path, use select path or select -unique path to remove duplicates:

Get-ChildItem -recurse | Get-Content | Select-String -pattern "dummy" | select -unique path
manojlds
  • 290,304
  • 63
  • 469
  • 417
  • 1
    Thanks to you both, this is exactly what I am looking for. Unfortunately, when there are many subdirectories involved in the path, then PS cuts the absolute path and adds three dots at the end of the line like \dir1\dir2\dir3\path... so I don't know which file is returned. Is there a way to tell PS to be less greedy on characters and bother showing up the full path ? :) Thanks a lot ! – Bluz Nov 18 '11 at 12:07
  • 3
    You need to add the `-File` switch to `Get-ChildItem` or you end up with a never ending cascade of errors from trying to call `Get-Content` on directories. – RubberDuck Feb 09 '16 at 13:58
  • If you need the full path you can do this `(Get-ChildItem -recurse | Get-Content | Select-String -pattern "dummy").FullName` People seem to forget PowerShell is object oriented; when in doubt, look for a property – Kellen Stuart Sep 06 '18 at 16:51
11

I modified one of the answers above to give me a bit more information. This spared me a second query later on. It was something like this:

Get-ChildItem `
        -Path "C:\data\path" -Filter "Example*.dat" -recurse | `
    Select-String -pattern "dummy" | `
    Select-Object -Property Path,LineNumber,Line | `
    Export-CSV "C:\ResultFile.csv"

I can specify the path and file wildcards with this structures, and it saves the filename, line number and relevant line to an output file.

RenoGreg
  • 111
  • 1
  • 2
  • 1
    Cool! Thanks for this additional solution :) However, could you please link to the answer that you based your solution on and name the author of that answer to give credit? Thanks! – Max Vollmer Mar 28 '19 at 00:54
9
Get-ChildItem -r | ? {$_.psiscontainer -eq $false} | ? {gc $_.pspath |select-string -pattern "dummy"}

This will give you the full details of all files

vikas368
  • 1,408
  • 2
  • 10
  • 13
6

To keep the complete file details in resulting array you could use a slight modification of the answer posted by vikas368 (which didn't seem to work well with the ISE autocomplete):

Get-ChildItem -Recurse | Where-Object { $_ | Select-String -Pattern "dummy" }

or in short:

ls -r | ?{ $_ | Select-String -Pattern "dummy" }
swalex
  • 3,885
  • 3
  • 28
  • 33
6

With PowerShell, go to the path where your files are and then type this command and replace ENTER THE STRING YOU SEARCH HERE (but keep the double quotes):

findstr /S /I /M /C:"ENTER THE STRING YOU SEARCH HERE" *.*

Have a nice day

5

If you search into one directory, you can do it:

select-string -Path "c:\temp\*.*" -Pattern "result"  -List | select Path
Esperento57
  • 16,521
  • 3
  • 39
  • 45
4

This will display a list of the full path to each file that contains the search string:

foreach ($file in Get-ChildItem | Select-String -pattern "dummy" | Select-Object -Unique path) {$file.path}

Note that it doesn't display a header above the results and doesn't display the lines of text containing the search string. All it tells you is where you can find the files that contain the string.

-1

This Scrit worked to find a specific file in a 3 000 000

Param
(

     #Define o parametro do ano a eliminar "2020"
     $DateDel = '2019',
     
     #Define o parametro do registro do ficheiro "_800" ou "_800sm" ou "_200"
     $ResFile1 = '_200',
     $ResFile2 = '_800',
     $ResFile3 = '_800sm',

     #Define o parametro da terminacao do ficheiro "_800.jpg" ou "_800sm.jpg" ou "_200.jpg"
     $TypeFile = '.jpg',
     
     #Define o parametro de onde se localizado ficheiro "C:\users\Luis.Cunha\Desktop\LuisCunha\TarefaScript\TesteFinal\TesteScript1"
     $HomePath = 'C:\Users\Luis.Cunha\Desktop\LuisCunha\TarefaScript'   
)

 #Inicia transcriçao de toda informação para o ficheiro .log indicado
 Start-Transcript -Path $HomePath\CountDelItems.log -NoClobber -Append

 Get-ChildItem $HomePath -Recurse -File | Measure-Object | %{$_.Count}

 #o Get vai buscar o ficheiro com a data e a terminacao definidas no $homepath e $tipofich atraves do caminho indicado no $path
 #depois confirma os valores que foram removidos com o verbose
Get-Childitem -Path $HomePath -Recurse -force | Where-Object { !$_.PSIsContainer -and $_.name -like "????$DateDel*$ResFile1$TypeFile" } | Measure-Object | %{$_.Count}
    
 #Get-Childitem -Path $HomePath -Recurse -force | Where-Object { !$_.PSIsContainer -and $_.name -like "????$DateDel*$ResFile1$TypeFile" } | Remove-Item -Verbose -Force 

 Get-ChildItem $HomePath -Recurse -File | Measure-Object | %{$_.Count}


 #Termina transcrição
 Stop-Transcript 
abdo Salm
  • 1,678
  • 4
  • 12
  • 22
  • This doesn't address the question, which is asking to retrieve the _paths_ of files with matching _contents_, **not** the _count_ of files with matching _names_ as this is doing. – Lance U. Matthews Sep 29 '22 at 04:15