Last active
August 6, 2025 16:06
-
-
Save Bill-Stewart/6b8e54e17526a0e6f7fafe432ca8a397 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Update-FileText.ps1 | |
| # Written by Bill Stewart (bstewart AT iname.com) | |
| #requires -version 3 | |
| # Version history: | |
| # | |
| # 2025-08-06 | |
| # * Added support for more file encodings (UTF8 default). | |
| # * Added -InputEncoding parameter. | |
| # * Cleaned up error handling. | |
| # | |
| # 2020-10-09 | |
| # * Initial version. | |
| <# | |
| .SYNOPSIS | |
| Updates text in files using a simple string or regular expression. | |
| .DESCRIPTION | |
| Updates text in files using a simple string or regular expression. Each file is read entirely into memory to support multi-line searching and replacing, so performance may be slow for large files. | |
| .PARAMETER Pattern | |
| Specifies the regular expression pattern. | |
| .PARAMETER Replacement | |
| Specifies the regular expression replacement pattern. | |
| .PARAMETER Path | |
| Specifies the path to one or more files. Wildcards are not supported. | |
| .PARAMETER CaseSensitive | |
| Specifies case-sensitive matching. The default is to ignore case. | |
| .PARAMETER SimpleMatch | |
| Specifies a simple match rather than a regular expression match (i.e., the Pattern parameter specifies a simple string rather than a regular expression). | |
| .PARAMETER Multiline | |
| Changes the meanings of ^ and $ so they match at the beginning and end, respectively, of any line, and not just the beginning and end of the entire file. The default is that ^ and $, respectively, match the beginning and end of the entire file. | |
| .PARAMETER UnixText | |
| Causes $ to match only linefeed (\n) characters. By default, $ matches carriage return+linefeed (\r\n). (Windows-based text files usually use \r\n as line terminators, while Unix-based text files usually use only \n.) | |
| .PARAMETER Overwrite | |
| Overwrites a file by creating a temporary file containing all replacements and then replacing the original file with the temporary file. The default is to output but not overwrite. | |
| .PARAMETER Force | |
| Allows overwriting of read-only files. Note that this parameter cannot override security restrictions. | |
| .PARAMETER InputEncoding | |
| Specifies the encoding of the file when reading it. Possible values for this parameter: | |
| - 'ANSI' - Uses the encoding for the for the current culture's ANSI code page | |
| - 'ASCII' - Uses the encoding for the ASCII (7-bit) character set | |
| - 'BigEndianUnicode' - Encodes in UTF-16 format using the big-endian byte order | |
| - 'BigEndianUTF32' - Encodes in UTF-32 format using the big-endian byte order | |
| - 'OEM' - Uses the default encoding for MS-DOS and console programs | |
| - 'Unicode' - Encodes in UTF-16 format using the little-endian byte order | |
| - 'UTF8' - Encodes in UTF-8 format without Byte Order Mark (BOM) | |
| - 'UTF8BOM' - Encodes in UTF-8 format with BOM | |
| - 'UTF8NoBOM' - Same as UTF8 | |
| - 'UTF32' - Encodes in UTF-32 format using the little-endian byte order | |
| The default value is 'UTF8'. | |
| .PARAMETER Encoding | |
| Specifies the encoding for the file when -Overwrite is used. The default value is UTF8. See the -InputEncoding parameter for possible values for this parameter. The default value is 'UTF8'. | |
| .LINK | |
| about_Regular_Expressions | |
| .EXAMPLE | |
| PS > Update-FileText.ps1 '(Ferb) and (Phineas)' '$2 and $1' Story.txt | |
| This command replaces the text 'Ferb and Phineas' with the text 'Phineas and Ferb' in the file Story.txt and outputs the content. Note that the pattern and replacement strings are enclosed in single quotes to prevent variable expansion. | |
| .EXAMPLE | |
| PS > Update-FileText.ps1 'Perry' 'Agent P' Story2.txt -Overwrite | |
| This command replaces the text 'Perry' with the text 'Agent P' in the file Story2.txt. | |
| #> | |
| [CmdletBinding(SupportsShouldProcess,ConfirmImpact = "High")] | |
| param( | |
| [Parameter(Mandatory,Position = 0,ValueFromPipeline,ValueFromPipelineByPropertyName)] | |
| [String[]] | |
| $Path, | |
| [Parameter(Mandatory,Position = 1)] | |
| [String] | |
| $Pattern, | |
| [Parameter(Mandatory,Position = 2)] | |
| [AllowEmptyString()] | |
| [String] | |
| $Replacement, | |
| [Switch] | |
| $CaseSensitive, | |
| [Switch] | |
| $SimpleMatch, | |
| [Switch] | |
| $Multiline, | |
| [Switch] | |
| $UnixText, | |
| [Switch] | |
| $Overwrite, | |
| [Switch] | |
| $Force, | |
| [String] | |
| [ValidateSet('ANSI','ASCII','BigEndianUnicode','BigEndianUTF32','OEM','Unicode','UTF8','UTF8BOM','UTF8NoBOM','UTF32')] | |
| $InputEncoding = 'UTF8', | |
| [String] | |
| [ValidateSet('ANSI','ASCII','BigEndianUnicode','BigEndianUTF32','OEM','Unicode','UTF8','UTF8BOM','UTF8NoBOM','UTF32')] | |
| $Encoding = 'UTF8' | |
| ) | |
| begin { | |
| function Get-TempName { | |
| [CmdletBinding()] | |
| param( | |
| [Parameter(Position = 0,Mandatory)] | |
| [String] | |
| $path | |
| ) | |
| do { | |
| $tempName = Join-Path $path ([IO.Path]::GetRandomFilename()) | |
| } | |
| while ( Test-Path $tempName ) | |
| $tempName | |
| } | |
| # In Text.UnicodeEncoding and Text.UTF32Encoding constructors: | |
| # 1st bool = big endian, 2nd = BOM | |
| function Get-Encoding { | |
| [CmdletBinding()] | |
| param( | |
| [String] | |
| [ValidateSet('ANSI','ASCII','BigEndianUnicode','BigEndianUTF32','OEM','Unicode','UTF8','UTF8BOM','UTF8NoBOM','UTF32')] | |
| $encoding | |
| ) | |
| $codePages = @{ | |
| 'ANSI' = [Text.Encoding]::GetEncoding(0) # default | |
| 'ASCII' = [Text.Encoding]::GetEncoding(20127) | |
| 'BigEndianUnicode' = New-Object Text.UnicodeEncoding($true,$true) | |
| 'BigEndianUTF32' = New-Object Text.UTF32Encoding($true,$true) | |
| 'OEM' = [Text.Encoding]::GetEncoding(437) | |
| 'Unicode' = New-Object Text.UnicodeEncoding($false,$true) | |
| 'UTF8' = New-Object Text.UTF8Encoding($false) | |
| 'UTF8BOM' = New-Object Text.UTF8Encoding($true) | |
| 'UTF32' = New-Object Text.UTF32Encoding($false,$true) | |
| } | |
| $codePages['UTF8NoBOM'] = $codePages['UTF8'] | |
| return $codePages[$encoding] | |
| } | |
| if ( $SimpleMatch ) { | |
| $Pattern = [Regex]::Escape($Pattern) | |
| } | |
| else { | |
| if ( -not $UnixText ) { | |
| $Pattern = $Pattern -replace '(?<!\\)\$','\r$' | |
| } | |
| } | |
| function New-Regex { | |
| $regexOpts = [Text.RegularExpressions.RegexOptions]::None | |
| if ( -not $CaseSensitive ) { | |
| $regexOpts = $regexOpts -bor [Text.RegularExpressions.RegexOptions]::IgnoreCase | |
| } | |
| if ( $Multiline ) { | |
| $regexOpts = $regexOpts -bor [Text.RegularExpressions.RegexOptions]::Multiline | |
| } | |
| New-Object Text.RegularExpressions.Regex $Pattern,$regexOpts | |
| } | |
| $Regex = New-Regex | |
| function Update-FileText { | |
| [CmdletBinding()] | |
| param( | |
| [Parameter(Position = 0,Mandatory)] | |
| [String] | |
| $path | |
| ) | |
| $pathInfo = Resolve-Path -LiteralPath $path | |
| if ( $null -eq $pathInfo ) { return } | |
| if ( (Get-Item (Resolve-Path $pathInfo).ProviderPath) -isnot [IO.FileInfo] ) { | |
| Write-Error "'$path' is not in the file system." -Category InvalidType | |
| return | |
| } | |
| $textInputEncoding = Get-Encoding $InputEncoding | |
| if ( $null -eq $textInputEncoding ) { return } | |
| $textOutputEncoding = Get-Encoding $Encoding | |
| if ( $null -eq $textOutputEncoding ) { return } | |
| $fullName = $pathInfo.ProviderPath | |
| $content = [IO.File]::ReadAllText($fullName,$textInputEncoding) | |
| if ( -not $content ) { return } | |
| if ( -not $Overwrite ) { | |
| $regex.Replace($content,$Replacement) | |
| return | |
| } | |
| $tempName = Get-TempName (Split-Path $fullName -Parent) | |
| Set-Content $tempName $null -Confirm:$false -Verbose:$VerbosePreference | |
| if ( -not $? ) { return } | |
| try { | |
| [IO.File]::WriteAllText($tempName,$Regex.Replace($content,$Replacement),$textOutputEncoding) | |
| Copy-Item $tempName $fullName -ErrorAction:Stop -Force:$Force -Verbose:$VerbosePreference | |
| } | |
| catch { | |
| Write-Error $_.Exception | |
| } | |
| if ( Test-Path $tempName ) { | |
| Remove-Item $tempName -Verbose:$VerbosePreference | |
| } | |
| } | |
| } | |
| process { | |
| foreach ( $PathItem in $Path ) { | |
| if ( $Overwrite ) { | |
| if ( $PSCmdlet.ShouldProcess("'$PathItem'","Overwrite file") ) { | |
| Update-FileText $PathItem | |
| } | |
| } | |
| else { | |
| Update-FileText $PathItem | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment