Passing a function to Powershell's (replace) function
Asked Answered
C

3

14

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

Canvass answered 17/11, 2011 at 6:34 Comment(0)
F
25

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
Fucoid answered 17/11, 2011 at 7:3 Comment(0)
W
10

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})
Wasteland answered 17/11, 2011 at 6:54 Comment(0)
T
1

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 ++.

      • For brevity, you can omit -Scope 1, and an even shorter - albeit obscure - alternative is to use ++([ref] $i).Value - see this answer for background information.
Thanet answered 29/3, 2024 at 23:21 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.