18

I'm trying to replace part of a string in Powershell. However, the replacement string is not hardcoded, it's calculated from a function:

$text = "the image is -12345-"
$text = $text -replace "-(\d*)-", 'This is the image: $1'
Write-Host $text

This gives me the correct result: "This is the image: 12345"

Now, I want to include the base64 encoded image. I can read the image from the id. I was hoping the following would work, but it doesn't:

function Get-Base64($path)
{
    [convert]::ToBase64String((get-content $path -encoding byte))
}
$text -replace "-(\d*)-", "This is the image: $(Get-Base64 '$1')"

The reason that it doesn't work, is because it first passes $1 (the string, not the value of $1) to the function, executes it and only then does it do the replace. What I want to do is

  • Find the occurrence of the pattern
  • Replace each occurence with the pattern
  • For each replace:
  • Pass the capture group to the function
  • Use the value of the capture group to get the base64 image
  • inject the base64 image into the replacement

3 Answers 3

28

You can use the static Replace method from the [regex] class:

[regex]::Replace($text,'-(\d*)-',{param($match) "This is the image: $(Get-Base64 $match.Groups[1].Value)"})

Alternatively you can define a regex object and use the Replace method of that object:

$re = [regex]'-(\d*)-'
$re.Replace($text, {param($match) "This is the image: $(Get-Base64 $match.Groups[1].Value)"})

For better readability you could define the callback function (the scriptblock) in a separate variable and use that in the replacement:

$callback = {
  param($match)
  'This is the image: ' + (Get-Base64 $match.Groups[1].Value)
}

$re = [regex]'-(\d*)-'
$re.Replace($text, $callback)
Sign up to request clarification or add additional context in comments.

1 Comment

For those looking to understand the derivation of this, it is using a sigature of the Replace method ( Regex.Replace Method (String, MatchEvaluator) ) that adds even more power to regexs by allowing computation on a matched argument. The neat thing is--which I did not realize until I saw this answer--is that a PowerShell script block is apparently compatible with a MatchEvaluator argument!
16

Note: As for why your own, string-based attempt didn't work: see the explanation in the bottom section of this answer.


PetSerAl's helpful answer, which relies on a direct call to a .NET API, is your only option in Windows PowerShell, the legacy, ships-with-Windows, Windows-only edition of PowerShell whose latest and last version is 5.1.

PowerShell (Core) 7, its modern, cross-platform, install-on-demand successor, now offers a native PowerShell solution via an enhancement to the -replace operator, which obviates the need to call [regex]::Replace():

Just as with [regex]::Replace(), you can now:

Applied to your case:

# PowerShell 7 only
$text -replace '-(\d*)-', { "This is the image: $(Get-Base64 $_.Groups[1].Value)" }

A simpler, self-contained example:

# PowerShell 7 only
# Increment the number embedded in a string:
# -> '43 years old'
'42 years old' -replace '\d+', { [int] $_.Value + 1 }

3 Comments

This should be the accepted answer
PS> '42 years old' -replace '\d+', { [int] $_.Value + 1 } returns [int] 42 years old.Value + 1 years old
@Ωmega, that's what happens in Windows PowerShell, where using script blocks as substitution operand isn't supported (the script block is simply stringified there, meaning that its verbatim contents (sans { and }) are used. As the answer notes, you need PowerShell (Core), version 6.1 or above (current is v7.3.8).
3

Here's another way. Use the -match operator, and then reference $matches. Note that $matches doesn't get set with arrays on the left side of the -match operator. $matches.1 is the first grouping formed by the ( ).

$text = "the image is -12345-"
function Get-Base64($path) { 
  [convert]::ToBase64String( (get-content $path -asbytestream) ) }  # ps 6 ver
if (! (test-path 12345)) { echo hi > 12345 }
$text -match '-(\d*)-'
$text -replace '-(\d*)-', "$(Get-Base64 $matches.1)"

the image is aGkNCg==

Or to break it up even more:

$text -match '-(\d*)-'
$result = Get-Base64 $matches.1
$text -replace '-(\d*)-', $result

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.