I suggest:
Reading the entire input file as a single string with Get-Content
's -Raw
switch.
Using -replace
/ [regex]::Replace()
with a script block to determine the substitution text, which allows you to increment a counter variable every time a replacement is made.
Note: Since you're replacing the input file with the results, be sure to make a backup copy first, to be safe.
In PowerShell (Core) 7+, the -replace
operator now directly accepts a script block that allows you to determine the substitution text dynamically:
$count = 0
(Get-Content -Raw $filePath) -replace $oldValue, { $newValue; ++$count } |
Set-Content -NoNewLine $filePath
$count
now contains the number of replacements, across all lines (including multiple matches on the same line), that were performed.
In Windows PowerShell, direct use of the underlying .NET API, [regex]::Replace()
, is required:
$count = 0
[regex]::Replace(
'' + (Get-Content -Raw $filePath),
$oldValue,
{ $newValue; ++(Get-Variable count).Value }
) | Set-Content -NoNewLine $filePath
Note:
'' +
ensures that the call succeeds even if file $filePath
has no content at all; without it, [regex]::Replace()
would complain about the argument being null.
++(Get-Variable count).Value
must be used in order to increment the $count
variable in the caller's scope (Get-Variable
can retrieve variables defined in ancestral scopes; -Scope 1
is implied here, thanks to PowerShell's dynamic scoping). Unlike with -replace
in PowerShell 7+, the script block runs in a child scope.
As an aside:
- For this use case, the only reason a script block is used is so that the counter variable can be incremented - the substitution text itself is static. See this answer for an example where the substitution text truly needs to be determined dynamically, by deriving it from the match at hand, as passed to the script block.