Skip to content

Instantly share code, notes, and snippets.

@Bill-Stewart
Last active August 6, 2025 16:06
Show Gist options
  • Select an option

  • Save Bill-Stewart/6b8e54e17526a0e6f7fafe432ca8a397 to your computer and use it in GitHub Desktop.

Select an option

Save Bill-Stewart/6b8e54e17526a0e6f7fafe432ca8a397 to your computer and use it in GitHub Desktop.
# 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