Reputation: 437
I want to pass a function call(which returns a string) as a replacement string to Powershell's replace function such that each match found is replaced with a different string.
Something like -
$global_counter = 0
Function callback()
{
$global_counter += 1
return "string" + $global_counter
}
$mystring -replace "match", callback()
Python allows this through 're' module's 'sub' function which accepts a callback function as input. Looking for something similar
Upvotes: 14
Views: 4250
Reputation: 438378
Many moons later, let me complement the existing, helpful answers - which continue to work in principle (see below) - with a simpler PowerShell (Core) 7 solution:
Thanks to a contribution by Mathias R. Jessen, PowerShell (Core) 7, the cross-platform successor to the legacy, Windows-only Windows PowerShell (whose latest an last version is 5.1):
now does support passing passing a callback as the substitution operand of the -replace
operator, ...
... namely in the form of a script block ({ ... }
), inside of which the match at hand can be referred to via the automatic $_
variable; $_.Value
therefore refers to the matched text.
Therefore:
# PowerShell 7 only
$i = 0
'zzz match match xxx' -replace 'match', { 'string-{0}-{1}' -f $_.Value, ++$i }
Output:
zzz string-match-1 string-match-2 xxx
Note the use of -f
, the format operator, to synthesize the substitution string that is output by the script block.
Since the substitution script block runs directly in the caller's scope, you can apply ++
, the increment operator, directly to the caller's $i
variable.
The backward-compatible Windows PowerShell-compatible solution that is the equivalent of the above is the following, using [regex]::Replace()
:
# Works in both Windows PowerShell and PowerShell 7.
$i = 0
[regex]::Replace(
'zzz match match xxx',
'match',
{ param($m) 'string-{0}-{1}' -f $m.Value, ++(Get-Variable -Scope 1 -Name i).Value }
)
The script block ({ ... }
) that is passed acts as the MatchEvaluator delegate, to which the match at hand is passed as an argument, which param($m)
inside the script block formally declares.
Unlike with -replace
in PowerShell 7, the script block runs in a child scope of the caller, requiring extra effort to update a variable in the caller's scope:
Get-Variable
is used with -Scope 1
to refer to the parent scope's $i
variable, whose .Value
property can then be incremented with ++
.
-Scope 1
, and an even shorter - albeit obscure - alternative is to use ++([ref] $i).Value
- see this answer for background information.Upvotes: 1
Reputation: 42043
Perhaps you are looking for Regex.Replace Method (String, MatchEvaluator). In PowerShell a script block can be used as MatchEvaluator
. Inside this script block $args[0]
is the current match.
$global_counter = 0
$callback = {
$global_counter += 1
"string-$($args[0])-" + $global_counter
}
$re = [regex]"match"
$re.Replace('zzz match match xxx', $callback)
Output:
zzz string-match-1 string-match-2 xxx
Upvotes: 25
Reputation: 354614
PowerShell does not (yet?) have support for passing a script block to the -replace
operator. The only option here is to use [Regex]::Replace
directly:
[Regex]::Replace($mystring, 'match', {callback})
Upvotes: 10