Powershell - 优化非常非常大的csv和文本文件搜索和替换

时间:2014-03-14 16:58:41

标签: search powershell csv optimization text

我有一个包含~3000个文本文件的目录,我正在定期搜索并替换这些文本文件,因为我将程序转换为新服务器。

每个文本文件平均可能有~3000行,我需要一次搜索文件大约300 - 1000个术语。

我正在替换与我正在搜索的字符串相关的服务器前缀。因此,对于每个csv条目,我正在寻找Search_String\\Old_Server\"Search_String"并确保在程序完成后,结果为"\\New_Server\Search_String"

我拼凑了一个PowerShell程序,它有效。但它太慢我从未见过它完整。

有什么建议让它更快?

编辑1: 我按照建议更改了get-content,但是仍然花了3分钟搜索两个文件(~8000行)以获得9个单独的搜索词。我还是要搞砸了;如果手动完成9次,记事本++搜索和替换仍然会更快。

我不知道如何摆脱第一个(Get-Content),因为我想在对其进行任何更改之前复制一份备份文件。

编辑2: 所以这快一个数量级;它在10秒钟内搜索文件。但现在它不会对文件进行更改,而只搜索目录中的第一个文件!我没有改变那段代码,所以我不知道它为什么会破坏。

编辑3: 成功!我调整了下面发布的解决方案,使其更快,更快。它现在在几秒钟内搜索每个文件。我可以反转循环顺序,以便将文件加载到数组中,然后搜索并替换CSV中的每个条目,而不是相反。如果我让它发挥作用我会张贴。

最终脚本在下面供参考。

#get input from the user
$old = Read-Host 'Enter the old cimplicity qualifier (F24, IRF3 etc'
$new = Read-Host 'Enter the new cimplicity qualifier (CB3, F24_2 etc)'
$DirName = Get-Date -format "yyyy_MM_dd_hh_mm"

New-Item -ItemType directory -Path $DirName -force
New-Item "$DirName\log.txt" -ItemType file -force -Value "`nMatched CTX files on $dirname`n"
$logfile = "$DirName\log.txt"

$VerbosePreference = "SilentlyContinue"


$points = import-csv SearchAndReplace.csv -header find #Import CSV File
#$ctxfiles = Get-ChildItem . -include *.ctx | select -expand fullname #Import local directory of CTX Files

$points | foreach-object { #For each row of points in the CSV file
    $findvar = $_.find #Store column 1 as string to search for  

    $OldQualifiedPoint = "\\\\"+$old+"\\" + $findvar #Use escape slashes to escape each invidual bs so it's not read as regex
    $NewQualifiedPoint = "\\"+$new+"\" + $findvar #escape slashes are NOT required on the new string
    $DuplicateNew = "\\\\" + $new + "\\" + "\\\\" + $new + "\\"
    $QualifiedNew = "\\" + $new + "\"

    dir . *.ctx | #Grab all CTX Files 
     select -expand fullname | #grab all of those file names and...
      foreach {#iterate through each file
                $DateTime = Get-Date -Format "hh:mm:ss"
                $FileName = $_
                Write-Host "$DateTime - $FindVar - Checking $FileName"
                $FileCopied = 0
                #Check file contents, and copy matching files to newly created directory
                If (Select-String -Path $_ -Pattern $findvar -Quiet ) {
                   If (!($FileCopied)) {
                        Copy $FileName -Destination $DirName
                        $FileCopied = 1
                        Add-Content $logfile "`n$DateTime - Found $Findvar in $filename"
                        Write-Host "$DateTime - Found $Findvar in $filename"
                    }

                    $FileContent = Get-Content $Filename -ReadCount 0
                    $FileContent =
                    $FileContent -replace $OldQualifiedPoint,$NewQualifiedPoint -replace $findvar,$NewQualifiedPoint -replace $DuplicateNew,$QualifiedNew
                    $FileContent | Set-Content $FileName
                }
           }
         $File.Dispose()
    }       

3 个答案:

答案 0 :(得分:2)

如果我正确读取此内容,您应该能够将3000行文件读入内存,并将这些替换作为数组操作,从而无需遍历每一行。您还可以将这些替换操作链接到单个命令中。

dir . *.ctx | #Grab all CTX Files 
     select -expand fullname | #grab all of those file names and...
      foreach {#iterate through each file
                $DateTime = Get-Date -Format "hh:mm:ss"
                $FileName = $_
                Write-Host "$DateTime - $FindVar - Checking $FileName"
                #Check file contents, and copy matching files to newly created directory
                If (Select-String -Path $_ -Pattern $findvar -Quiet ) {
                    Copy $FileName -Destination $DirName
                    Add-Content $logfile "`n$DateTime - Found $Findvar in $filename"
                    Write-Host "$DateTime - Found $Findvar in $filename"

                    $FileContent = Get-Content $Filename -ReadCount 0
                    $FileContent =
                      $FileContent -replace $OldQualifiedPoint,$NewQualifiedPoint -replace $findvar,$NewQualifiedPoint -replace $DuplicateNew,$QualifiedNew
                     $FileContent | Set-Content $FileName
                }
           }

另一方面,Select-String将文件路径作为参数,因此您不必执行Get-Content,然后将其传递给Select-String

答案 1 :(得分:1)

是的,您可以通过不使用Get-Content来更快地使用...而是使用Stream Reader。

$file = New-Object System.IO.StreamReader -Arg "test.txt"
while (($line = $file.ReadLine()) -ne $null) {
    # $line has your line
}
$file.dispose()

答案 2 :(得分:0)

我想使用PowerShell并创建一个类似下面的脚本:

$filepath = "input.csv"
$newfilepath = "input_fixed.csv"

filter num2x { $_ -replace "aaa","bbb" }
measure-command {
    Get-Content -ReadCount 1000 $filepath | num2x | add-content $newfilepath
}    

我的笔记本电脑花了19分钟来处理6.5Gb文件。下面的代码是批量读取文件(使用ReadCount)并使用应该优化性能的过滤器。

然后我尝试了FART并在3分钟内做了同样的事情!相当不同!