Try this:
$c = [ref] 0
$text = Get-Content 'C:\Users\H\Desktop\3.txt' -Raw
[regex]::Replace( $text, 'num', { (++$c.Value) } ) |
Set-Content 'C:\Users\H\Desktop\4.txt'
# With PowerShell 6+ you could write:
# (Get-Content 'C:\Users\H\Desktop\3.txt' -Raw) -replace 'num', { (++$c.Value) } |
# Set-Content 'C:\Users\H\Desktop\4.txt'
Copy-pastable demo:
$text = @'
num foo num
bar num bar
num baz num
'@
$c = [ref] 0
[regex]::Replace( $text, 'num', { (++$c.Value) } )
# With PowerShell 6+ you could write:
# $text -replace 'num', { (++$c.Value) }
Output:
1 foo 2
bar 3 bar
4 baz 5
Explanation:
- Use a reference type (
[ref]) variable instead of a plain variable. The scriptblock passed to [regex]::Replace() or -replace (PS 6+ only) runs in a child scope, so it can't modify variables from the parent scope directly. You can modify the members of a reference type, so this is why the [ref] trick works. Instead of [ref] you could also use a [PSCustomObject] or a [Hashtable] (which are both reference types) with a counter member.
- The parenthesis around the expression
++$c.Value are required to output the value of the expression, which doesn't produce output by default. You already had that, I'm just explaining it for other visitors.
- Using
Get-Content with parameter -Raw can be faster, because the file's content gets output as a single multiline string instead of splitting it up into one string per line, at the expense of requiring more memory.
As for what you have tried:
$_ -replace "num", $((++$c))
You are passing an expression instead of a script block as the RHS argument to the -replace operator. Furthermore, a script block argument for the -replace operator is only supported beginning with PowerShell 6. For older versions you have to use the .NET function [Regex]::Replace.
This expression is evaluated once for each iteration of the ForEach-Object "loop", before the -replace operator is evaluated. So you are effectively just counting the lines of the file, not the number of occurences of the pattern.
Only a script block's execution can be delayed. It doesn't get called immediately in the place where you define it1, but when the function or operator that has a [ScriptBlock] parameter decides to call it, possibly multiple times as happens when the pattern has multiple matches. To define a script block, use {} as I did in my sample at the beginning.
[1] Unless you use the call or dot source operator, e.g. &{'foo'}