Powershell: Replacing regex named groups with variables Powershell: Replacing regex named groups with variables powershell powershell

Powershell: Replacing regex named groups with variables


Simple Solution

In the scenario where you simply want to replace a version number found somewhere in your $input text, you could simply do this:

$input -replace '(Version\s+)\d+,\d+,\d+,\d+',"`$1$Version1,$Version2,$Version3,$Version4"

Using Named Captures in PowerShell

Regarding your question about named captures, that can be done by using curly brackets. i.e.

'dogcatcher' -replace '(?<pet>dog|cat)','I have a pet ${pet}.  '

Gives:

I have a pet dog.  I have a pet cat.  cher

Issue with multiple captures & solution

You can't replace multiple values in the same replace statement, since the replacement string is used for everything. i.e. if you did this:

 'dogcatcher' -replace '(?<pet>dog|cat)|(?<singer>cher)','I have a pet ${pet}.  I like ${singer}''s songs.  '

You'd get:

I have a pet dog.  I like 's songs.  I have a pet cat.  I like 's songs.  I have a pet .  I like cher's songs.  

...which is probably not what you're hoping for.

Rather, you'd have to do a match per item:

'dogcatcher' -replace '(?<pet>dog|cat)','I have a pet ${pet}.  ' -replace '(?<singer>cher)', 'I like ${singer}''s songs.  ' 

...to get:

I have a pet dog.  I have a pet cat.  I like cher's songs.  

More Complex Solution

Bringing this back to your scenario, you're not actually using the captured values; rather you're hoping to replace the spaces they were in with new values. For that, you'd simply want this:

$input = 'I''m running Programmer''s Notepad version 2.4.2.1440, and am a big fan.  I also have Chrome v    56.0.2924.87 (64-bit).' $version1 = 1$version2 = 3$version3 = 5$version4 = 7$v1Pattern = '(?<=\bv(?:ersion)?\s+)\d+(?=\.\d+\.\d+\.\d+)'$v2Pattern = '(?<=\bv(?:ersion)?\s+\d+\.)\d+(?=\.\d+\.\d+)'$v3Pattern = '(?<=\bv(?:ersion)?\s+\d+\.\d+\.)\d+(?=\.\d+)'$v4Pattern = '(?<=\bv(?:ersion)?\s+\d+\.\d+\.\d+\.)\d+'$input -replace $v1Pattern, $version1 -replace $v2Pattern, $version2 -replace $v3Pattern,$version3 -replace $v4Pattern,$version4

Which would give:

I'm running Programmer's Notepad version 1.3.5.7, and am a big fan.  I also have Chrome v    1.3.5.7 (64-bit).

NB: The above could be written as a 1 liner, but I've broken it down to make it simpler to read.

This takes advantage of regex lookarounds; a way of checking the content before and after the string you're capturing, without including those in the match. i.e. so when we select what to replace we can say "match the number that appears after the word version" without saying "replace the word version".

More info on those here: http://www.regular-expressions.info/lookaround.html

Your Example

Adapting the above to work for your example (i.e. where versions may be separated by commas or dots, and there's no consistency to their format beyond being 4 sets of numbers:

$input = @'#define SOME_MACRO(4, 1, 0, 0)Version "1.2.3.4"SomeStruct vs = { 99,99,99,99 }'@$version1 = 1$version2 = 3$version3 = 5$version4 = 7$v1Pattern = '(?<=\b)\d+(?=\s*[\.,]\s*\d+\s*[\.,]\s*\d+\s*[\.,]\s*\d+\b)'$v2Pattern = '(?<=\b\d+\s*[\.,]\s*)\d+(?=\s*[\.,]\s*\d+\s*[\.,]\s*\d+\b)'$v3Pattern = '(?<=\b\d+\s*[\.,]\s*\d+\s*[\.,]\s*)\d+(?=\s*[\.,]\s*\d+\b)'$v4Pattern = '(?<=\b\d+\s*[\.,]\s*\d+\s*[\.,]\s*\d+\s*[\.,]\s*)\d+\b'$input -replace $v1Pattern, $version1 -replace $v2Pattern, $version2 -replace $v3Pattern,$version3 -replace $v4Pattern,$version4

Gives:

#define SOME_MACRO(1, 3, 5, 7)Version "1.3.5.7"SomeStruct vs = { 1,3,5,7 }


Regular expressions don't work that way, so you can't. Not directly, that is. What you can do (short of using a more appropriate regular expression that groups the parts you want to keep) is to extract the version string and then in a second step replace that substring with the new version string:

$oldver = $input -replace $regexp, '$1,$2,$3,$4'$newver = $input -replace $oldver, "$Version1,$Version2,$Version3,$Version4"

Edit:

If you don't even know the structure, you must extract that from the regular expression as well.

$version = @($version1, $version2, $version3, $version4)$input -match $regexp$oldver = $regexp$newver = $regexpfor ($i = 1; $i -le 4; $i++) {  $oldver = $oldver -replace "\(\?<version$i>\\d\)", $matches["version$i"]  $newver = $newver -replace "\(\?<version$i>\\d\)", $version[$i-1]}$input -replace $oldver, $newver