简体   繁体   中英

How to Find Replace Multiple strings in multiple text files using Powershell

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!

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM