18

I want to replace a text in multiple files and folders. The folder name changes, but the filename is always config.xml.

$fileName = Get-ChildItem "C:\config\app*\config.xml" -Recurse
(Get-Content $fileName) -replace 'this', 'that' | Set-Content $fileName

When I run the above script it works, but it writes the whole text in config.xml about 20 times. What's wrong?

Guy Thomas
  • 933
  • 4
  • 14
  • 31
aston_zh
  • 6,543
  • 4
  • 20
  • 23
  • possible duplicate of [PowerShell Script to Find and Replace for all Files with a Specific Extension](http://stackoverflow.com/questions/2837785/powershell-script-to-find-and-replace-for-all-files-with-a-specific-extension) – AncientSwordRage Sep 26 '15 at 10:45

4 Answers4

18

$filename is a collection of System.IO.FileInfo objects. You have to loop to get the content for each file : this should do what you want :

$filename | %{
    (gc $_) -replace "THIS","THAT" |Set-Content $_.fullname
}
Loïc MICHEL
  • 24,935
  • 9
  • 74
  • 103
  • Note: this seems to set the content of all files, i.e. image files will be opened as text and re-saved. I added an additional check before modifying files – Kieren Johnstone Mar 01 '17 at 10:13
  • The text "THIS" can contain a regular expression as well. So, if you want to replace a literal string "THIS+" with "THAT+" then you need to escape the + in the source string. E.g. "THIS\+","THAT+" – Amit Tendulkar Dec 07 '22 at 09:19
11

In general, you should use the pipeline and combine the ForEach-Object and/or Where-Object CmdLets.

In your case, this would like like something more akin to:

Get-ChildItem "C:\config\app*\config.xml" -Recurse | ForEach-Object -Process {
    (Get-Content $_) -Replace 'this', 'that' | Set-Content $_
}

Which can be shortened somewhat to:

dir "C:\config\app*\config.xml" -recurse |% { (gc $_) -replace 'this', 'that' | (sc $_) }
Maxime Labelle
  • 3,609
  • 2
  • 27
  • 48
6

$filename is an array of filenames, and it's trying to do them all at once. Try doing them one at a time:

$fileNames = Get-ChildItem "C:\config\app*\config.xml" -Recurse |
 select -expand fullname

foreach ($filename in $filenames) 
{
  (  Get-Content $fileName) -replace 'this', 'that' | Set-Content $fileName
}
mjolinor
  • 66,130
  • 7
  • 114
  • 135
0

I got list of files to replace text this way.

$filenames = Get-ChildItem|Select-String -Pattern ""|select Filename

This gets 12 files.

To replace this text in all files

foreach ($filename in $filesnames){ (Get-Content $filename.Filename) -replace "", ""|Set-Content $filename.Filename }

Don't forget last part for Filename. $filename.Filename

David Morrow
  • 262
  • 4
  • 9