2

I'm renaming files with a regex replacement:

ls *dat | rename-item -newname {$_.name -replace '(.*)_([0-9]+)(.dat)','$1($2)$3'}

but I want also to add a constant int to [string]$2; something like :

'$1(@{int.parse($2)+3})($3)'

How should I proceed ?

3
  • 4
    You need a callback function for that. Commented Jul 24, 2018 at 18:52
  • 2
    Another option (nice callback function comment above) is to use the -match operator: 'abc_123.dat' -match '(.*)_([0-9]+)(.dat)' | out-null;$matches[1] + "($(([int]::Parse($matches[2]) + 3)))" + $matches[3]; Commented Jul 24, 2018 at 19:02
  • i feel stupid reading this :) Commented Jul 24, 2018 at 19:41

1 Answer 1

3

PowerShell Core v6.1.0+ supports passing a script block as the replacement operand of the
-replace operator
; the script block is invoked for each match found, and its output forms the replacement string, thereby enabling algorithmic replacements (rather than merely textual ones):

'foo_10.dat' -replace '(.*)_([0-9]+)(\.dat)', { 
  '{0}{1}{2}' -f $_.Groups[1].Value, 
                 ([int] $_.Groups[2].Value + 3), 
                 $_.Groups[3].Value
}

The above yields:

foo13.dat

Note how 3 was added to 10 (and the underscore was removed):

$_ inside the script block is a [System.Text.RegularExpressions.Match] instance representing the results of the match; e.g., $_.Value represents the full match, whereas $_.Groups[1].Value represents what the 1st capture group matched.

The functionality is built on the [regex] .NET type's .Replace() method, which can also used directly (but less easily) in earlier PowerShell versions - see below.


In Windows PowerShell you have two options:

  • Use the -match operator first and then perform the transformation in a separate statement based on the match information reflected in the automatic $Matches variable, as suggested by kuujinbo:

    $null = 'foo_10.dat' -match '(.*)_([0-9]+)(\.dat)' # match first
    '{0}{1}{2}' -f $Matches.1, ([int] $Matches.2 + 3), $Matches.3 # build via $Matches
    
  • Use the .NET framework directly, with a script block passed as a delegate (callback function) to the above-mentioned [regex]::Replace() method, as suggested by Ansgar Wiechers:

    ([regex] '(.*)_([0-9]+)(\.dat)').Replace('foo_10.dat', {
      param($match)
      '{0}{1}{2}' -f $match.Groups[1].Value, ([int] $match.Groups[2].Value + 3), $match.Groups[3].Value
    })
    
    • Note how a formal parameter - param($match) - must be declared here in order to access the match results, whereas the pure PowerShell solution at the top was able to use $_, as usual.
Sign up to request clarification or add additional context in comments.

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.