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

3 Answers 3

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
Sign up to request clarification or add additional context in comments.

Comments

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})

Comments

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.