Reputation: 25
So I working with a script that looks like this. It works just fine the only issue is that it is counting the line numbers and I just wanted it to replace num each time with 1 2 3 4 etc insted it is looking like this 5 20 25 etc and that is because it seems to be counting and incremnting $c for each line not each time it replaces the string num.
$c=0
(Get-Content C:\Users\H\Desktop\3.txt) |
Foreach-Object {$_ -replace "num", $((++$c))} |
Out-File C:\Users\H\Desktop\4.txt
Upvotes: 1
Views: 592
Reputation: 27756
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:
[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.++$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.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'}
Upvotes: 2