pf-string-replace.ps1
function Update-String_Replace_Multiple { [CmdLetBinding()] param ( [HashTable]$replacements, [switch]$all, [Parameter(ValueFromPipeline=$true)] [String]$src ) begin { $rep = @() if ($replacements) { $rep += $replacements.GetEnumerator() | ForEach-Object { [PSCustomObject]@{Find=[RegEx]::Escape($_.Key); ReplaceWith = $_.Value; length = $_.Key.Length } } # Gives priority to longer values to replace $rep = $rep | Sort-Object Find -Descending | Sort-Object Length -Descending } } process { foreach ($r in $rep) { $result = $src -ireplace $r.Find, $r.ReplaceWith if ( -not $all -and ( $result -ne $src) ) { return $result } $src = $result } return $src } } function Update-String_Replace_Multiple:::Test { 'a/b/c/d/' | Update-String_Replace_Multiple | assert 'a/b/c/d/' 'a/b/c\d/' | Update-String_Replace_Multiple -all @{'b/'='2'; 'c\'='3'} | assert 'a/23d/' 'a/b/c/d/' | Update-String_Replace_Multiple -all @{'b'='2'; d='4'} | assert 'a/2/c/4/' 'a/b/c/d/' | Update-String_Replace_Multiple -all @{'B'='2'; d='4'} | assert 'a/2/c/4/' } function Update-FileContent_Replace_Multiple { [CmdLetBinding()] param( [Parameter(Mandatory=$true, ValueFromPipeline=$true)] $Path, [HashTable]$replacements, [switch]$all, [switch]$PSExpressions, $destination ) process { $path = Get-Path $path if ($path) { if (-not $destination) { $destination = $path } $enc = Get-FileEncoding -FullName $path $content = ( Get-Content -Raw -Path $path ) $newContent = $content | Update-String_Replace_Multiple -replacements $replacements -all:$all.IsPresent if ($newContent -ne $content) { Write-Verbose "Replacing from '$path' " Set-Content -Path $destination $newContent -Encoding $enc -NoNewline } } } } #Replace content in a file without changing the encoding function Update-FileContent_Replace { [CmdletBinding()] param ( [Parameter(Mandatory=$true)] $find, $replacement = '', [Parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] $file ) begin { $find = [regex]::Escape( $find ) $find = '(?ms)' + $find.Replace('\*','(.*?)') } process { $path = Get-Path $file if ($path) { $enc = Get-FileEncoding -FullName $path $content = ( Get-Content -Raw -Path $path ) $newContent = $content -replace $find, $replacement if ($PSExpressions) { $psExp = '"' + $newContent + '"' $newContent = Invoke-Expression -Command $psExp } if ($newContent -ne $content) { Write-Verbose "Replacing in '$path' , '$find' -> '$replacement' " Set-Content -Path $path $newContent -Encoding $enc -NoNewline } } } } function Update-FolderContent_Replace { [CmdletBinding()] param( [Parameter(Mandatory=$true, ValueFromPipeline=$true)] $folder, [Parameter(Mandatory=$true)] $currentValue, [Parameter(Mandatory=$true)] $newValue, $filter = '*.xml' ) process { $folder = Get-Path $folder # Replace Content $matcheList = Get-ChildItem -Path $folder -Include $filter -Recurse | Select-String -SimpleMatch $currentValue if ($matcheList) { $matcheList.Path | Update-FileContent_Replace $currentValue $newValue } # Rename files $torename = Get-ChildItem -Path $folder -fi "*$currentValue.*" -Recurse -File $torename | Rename-Item_Replace -currentValue $currentValue -newValue $newValue # Rename Folders $torename = Get-ChildItem -Path $folder -fi "*$currentValue.*" -Recurse -Directory $torename | Rename-Item_Replace -currentValue $currentValue -newValue $newValue } } function Invoke-PSTemplate { [CmdletBinding()] param ( [Parameter(ValueFromPipeline=$true)] $templateContent, $TemplateContext ) process { $quotesScaped = $templateContent.Replace('`','``') $quotesScaped = $quotesScaped.Replace('"','`"') try { $Content = Invoke-Expression "`"$quotesScaped`"" -ErrorAction SilentlyContinue } catch { # For now ignoring any errors $Content = $templateContent } return $Content } } #Replace content by executing Powershell expressions indicated in the file content function Update-File_Replace_PSTemplate { [CmdletBinding()] param ( [Parameter(ValueFromPipeline=$true)] $file, [ScriptBlock]$fileNameRenamer, [switch]$NoRename, $TemplateContext ) begin { if (-not $fileNameRenamer) { if ($NoRename) { $fileNameRenamer = { param($filename) $filename } } else { $fileNameRenamer = { param([string]$fullname) Get-NewName_Preextension $fullname } } } } process { $file = Get-Path $file $templateContent = Get-Content -Path $file -Raw $enc = Get-FileEncoding -FullName $file $Content = Invoke-PSTemplate -templateContent $templateContent -TemplateContext $TemplateContext $newFile = $fileNameRenamer.Invoke($file) Set-Content -Path $newFile -Value $Content -Encoding $enc -Verbose -NoNewline return $newFile } } function Update-File_Replace_PSTemplate:::Example { $testFolder = "$env:TEMP\Update-File_Replace_PSTemplate" New-Folder_EnsureExists -folder $testFolder -New $testFile = "$testFolder\input.sql.pstemplate" $testFile = "$testFolder\input" $templateContext = @{ A = 1; B = @{ C = 'AAA' } } Write-Debug $templateContext $content = @' $($templateContext.A) '$($templateContext.A)' "$($templateContext.A)" `$($templateContext.A)` $($templateContext.B.C) '@ Set-Content -Path $testFile -Value $content -NoNewline $newFile = $testFile | Update-File_Replace_PSTemplate -fileNameRenamer { param([string]$fullname) Get-NewName_Preextension $fullname } Write-Host $newFile Write-Host (Get-Content $newFile -Raw) } function Update-Replace_Block { [CmdletBinding()] param ( [Parameter(Mandatory=$true)] $startBlock, $endBlock, $replacement = '', [Parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] $content ) begin { $startGroup = 'start' $endGroup = 'end' $find = "(?<$startGroup>$startBlock\s*(`r?)`n)(?<mid>.*?)(?<$endGroup>(`r?)`n\s*$endBlock)" $regex = [Regex]$find $callback = { param($match) $startCaptured = $match.Groups[$startGroup].value $endCaptured = $match.Groups[$endGroup].value $startCaptured + $replacement + $endCaptured } } process { $newContent = $regex.Replace($content, $callback) return $newContent } } function Update-Replace_Block:::Test { $content = ' line 1 #start line mid #end line 2 ' $content | Update-Replace_Block -startBlock '#start' -endBlock '#end' -replacement 'replace' } function Update-FileContent_Replace_Block { [CmdletBinding()] param ( [Parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] $file, [Parameter(Mandatory=$true)] $startBlock, $endBlock, $replacement = '' ) begin { $startGroup = 'start' $endGroup = 'end' $find = "(?<$startGroup>$startBlock\s*(`r?)`n)(?<mid>.*?)(?<$endGroup>(`r?)`n\s*$endBlock)" $regex = [Regex]$find $callback = { param($match) $startCaptured = $match.Groups[$startGroup].value $endCaptured = $match.Groups[$endGroup].value $startCaptured + $replacement + $endCaptured } } process { $path = Get-Path $file if ($path) { $enc = Get-FileEncoding -FullName $path $content = [System.IO.File]::ReadAllText($path) $newContent = $regex.Replace($content, $callback) if ($newContent -ne $content) { Write-Verbose "Replacing in '$path' , '$find' -> '$replacement' " Set-Content -Path $path $newContent -Encoding $enc -NoNewline } } } } function Update-FileContent_Replace_Block:::Test { $testfile = "$env:TEMP\Update-FileContent_Replace_Block.txt" $content = ' line 1 #start line mid #end line 2 ' Set-Content -Value $content -Path $testfile -NoNewline Update-FileContent_Replace_Block -file $testfile -startBlock '#start' -endBlock '#end' -replacement 'replace' $expected = ' line 1 #start replace #end line 2 ' $actual = Get-Content -Path $testfile -Raw $actual | assert -eq $expected } #Replace content in a file without changing the encoding function Update-FileContent_Replace { [CmdletBinding()] param ( [Parameter(Mandatory=$true)] $find, $replacement = '', [Parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] $file ) begin { $find = [regex]::Escape( $find ) $find = '(?ims)' + $find.Replace('\*','(.*?)') $regex = [Regex]$find } process { $path = Get-Path $file if ($path) { $enc = Get-FileEncoding -FullName $path $content = [System.IO.File]::ReadAllText($path) $newContent = $regex.Replace($content, $replacement) if ($newContent -ne $content) { Write-Verbose "Replacing in '$path' , '$find' -> '$replacement' " Set-Content -Path $path -Value $newContent -Encoding $enc -NoNewline } } } } function Update-FileContent_Replace:::Test { $testfile = "$env:TEMP\Update-FileContent_Replace.txt" function GenTestContent ($lines = 10) { begin { @(1..$lines) } process { $_ } end { @(1..$lines) } } GenTestContent > $testfile $replacement = 'AAAA' $testfile | Update-FileContent_Replace '5' $replacement $newContent = Get-Content $testfile -Raw $newContent.Contains($replacement) | Assert $true # on line with * 'start between end' | GenTestContent > $testfile $testfile | Update-FileContent_Replace 'start * end' 'start MID end' $newContent = Get-Content $testfile -Raw $newContent.Contains('start MID end') | Assert $true # Multiline with * 'begin between inter', 'inter between close' | GenTestContent > $testfile $testfile | Update-FileContent_Replace 'begin * close' 'begin MID close' $newContent = Get-Content $testfile -Raw $newContent.Contains('begin MID close') | Assert $true # Multiline with * ' { between } ' | GenTestContent > $testfile $testfile | Update-FileContent_Replace '{ * }' '{ MID }' $newContent = Get-Content $testfile -Raw $newContent.Contains('{ MID }') | Assert $true # Multiline with * ' <# inter ', ' inter #> '| GenTestContent > $testfile $testfile | Update-FileContent_Replace '<# * #>' '<# MID #>' $newContent = Get-Content $testfile -Raw $newContent.Contains('<# MID #>') | Assert $true Remove-Item $testfile -Force } |