tags:

views:

146

answers:

1

I have a powershell script that is replacing patterns in a file with the argument passed to the script. I grabbed the algorithm from another site and it works great, except for the fact that when I use anything but a literal string, the pattern isn't replaced.

Here's the original script:

 (Get-Content c:\File.txt) | 
     Foreach-Object { $_ -replace "\*", "@" } | 
     Set-Content c:\File.txt

Here's my version (basically)

 (Get-Content ("c:\File-" + $args[0] + ".txt")) | 
     Foreach-Object { $_ -replace "\%pattern\%", $args[0] } | 
     Set-Content ("c:\File-" + $args[0] + ".txt")

The new file is created correctly, and all instances of %pattern% are replaced, but with a blank string, not the string in $args[0]. I am getting hit with a scoping problem for the $args variable?

+4  A: 

Yes. The Foreach-Object's scriptblock gets a new $args e.g.:

PS> function foo { $OFS=',';"func: $args"; 1 | Foreach {"foreach: $args"} }
PS> foo 1 2 3
func: 1,2,3
foreach:

This is easily solved with a temp variable:

$fargs = $args; 
(Get-Content ("c:\File-" + $args[0] + ".txt")) |
    Foreach-Object { $_ -replace "\%pattern\%", $fargs[0] } |      
    Set-Content ("c:\File-" + $args[0] + ".txt")

BTW if this is in a script, you can avoid this problem completely by using a named parameter like so:

param([string]$Pattern)
(Get-Content ("c:\File-$Pattern.txt")) |
    Foreach-Object { $_ -replace "\%pattern\%", $Pattern } |      
    Set-Content ("c:\File-$Pattern.txt")
Keith Hill
Just out of curiosity: Is that the case for all script blocks or special to `ForEach-Object`? If it's the latter, what's in the `$args`, actually?
Joey
AFAIK every scripblock gets a new $args. The OP says this code is in a script so $args would contain any unnamed parameters which begs the question, why not use a named parameter e.g. $Pattern instead of $args. That's the approach I would take. Think I'll tack this suggestion onto the answer.
Keith Hill
Oh, nice. I can actually pass arguments into a scriptblock. `.{Write-Host $args}1,2,3` does indeed work. I think I can shorten some of my golfed Project Euler solutions a little with that :-)
Joey
Even better, in V2 you can have named params like so `.{param($a,$b)Write-Host "a is $a, b is $b"}1 (get-date)`
Keith Hill