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
CodePudding user response:
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'}