1

I am new to scripting, and Powershell. I have been doing some study lately and trying to build a script to find/replace text in a bunch of text files (Each text file having code, not more than 4000 lines). However, I would like to keep the FindString and ReplaceString as variables, for there are multiple values, which can in turn be read from a separate csv file.

I have come up with this code, which is functional, but I would like to know if this is the optimal solution for the aforementioned requirement. I would like to keep the FindString and ReplaceString as regular expression compatible in the script, as I would also like to Find/Replace patterns. (I am yet to test it with Regular Expression Pattern)

Sample contents of Input.csv: (Number of objects in csv may vary from 50 to 500)

FindString  ReplaceString
AA1A    171PIT9931A
BB1B    171PIT9931B
CC1C    171PIT9931E
DD1D    171PIT9932A
EE1E    171PIT9932B
FF1F    171PIT9932E
GG1G    171PIT9933A

The Code

$Iteration = 0
$FDPATH = 'D:\opt\HMI\Gfilefind_rep'
#& 'D:\usr\fox\wp\bin\tools\fdf_g.exe' $FDPATH\*.fdf
$GraphicsList = Get-ChildItem -Path $FDPATH\*.g | ForEach-Object FullName
$FindReplaceList = Import-Csv -Path $FDPATH\Input.csv
foreach($Graphic in $Graphicslist){
    Write-Host "Processing Find Replace on : $Graphic"
    foreach($item in $FindReplaceList){
    Get-Content $Graphic | ForEach-Object { $_ -replace "$($item.FindString)", "$($item.ReplaceString)" } | Set-Content ($Graphic+".tmp")
        Remove-Item $Graphic
        Rename-Item ($Graphic+".tmp") $Graphic
        $Iteration = $Iteration +1
        Write-Host "String Replace Completed for $($item.ReplaceString)"
    }
}

I have gone through other posts here in Stackoverflow, and gathered valuable inputs, based on which the code was built. This post from Ivo Bosticky came pretty close to my requirement, but I had to perform the same on a nested foreach loop with Find/Replace Strings as Variables reading from an external source.

To summarize,

  1. I would like to know if the above code can be optimized for execution, since I feel it takes a long time to execute. (I prefer not using aliases for now, as I am just starting out, and am fine with a long and functional script rather than a concise one which is hard to understand)
  2. I would like to add the number of Iterations being carried out in the loop. I was able to add the current Iteration number onto the console, but couldn't figure how to pipe the output of Measure-Command onto a variable, which could be used in Write-Host Command. I would also like to display the time taken for code execution, on completion.

Thanks for the time taken to read this Query. Much appreciate your support!

Community
  • 1
  • 1
Sriram
  • 11
  • 1
  • 3
  • Aliases won't change the performance at all, and using aliases in scripts is an anti-pattern; the preferred practice is to use the full cmdlet & parameter names. Aliases are fine when using the interactive shell, but not saved scripts/modules you'll use in the future and in other places. – alroc Aug 06 '14 at 15:21
  • Fully Agree Alroc. I meant to say, while I requested the script to be optimized, I wanted it to be optimized for performance (efficiency) and not ways to shorten it, by using the respective aliases for the cmdlet's used. – Sriram Aug 06 '14 at 15:49

1 Answers1

0

First of all, unless your replacement string is going to contain newlines (which would change the line boundaries), I would advise getting and setting each $Graphic file's contents only once, and doing all replacements in a single pass. This will also result in fewer file renames and deletions.

Second, it would be (probably marginally) faster to pass $item.FindString and $item.ReplaceString directly to the -replace operator rather than invoking the templating engine to inject the values into string literals.

Third, unless you truly need the output to go directly to the console instead of going to the normal output stream, I would avoid Write-Host. See Write-Host Considered Harmful.

And fourth, you might actually want to remove the Write-Host that gets called for every find and replace, as it may have a fair bit of effect on the overall execution time, depending on how many replacements there are.

You'd end up with something like this:

$timeTaken = (measure-command {
    $Iteration = 0
    $FDPATH = 'D:\opt\HMI\Gfilefind_rep'
    #& 'D:\usr\fox\wp\bin\tools\fdf_g.exe' $FDPATH\*.fdf
    $GraphicsList = Get-ChildItem -Path $FDPATH\*.g | ForEach-Object FullName
    $FindReplaceList = Import-Csv -Path $FDPATH\Input.csv
    foreach($Graphic in $Graphicslist){
        Write-Output "Processing Find Replace on : $Graphic"
        Get-Content $Graphic | ForEach-Object { 
        foreach($item in $FindReplaceList){
            $_ = $_ -replace $item.FindString, $item.ReplaceString
        }       
        $Iteration += 1
        $_
        } | Set-Content ($Graphic+".tmp")
        Remove-Item $Graphic
        Rename-Item ($Graphic+".tmp") $Graphic
    }
}).TotalMilliseconds

I haven't tested it but it should run a fair bit faster, plus it will save the elapsed time to a variable.

Justin Dunlap
  • 181
  • 2
  • 4