2015-08-14 4 views
1

У меня есть список строк в CSV-файле. Формат:Использование Powershell для замены нескольких строк в нескольких файлах и папках

OldValue,NewValue 
223134,875621 
321321,876330 
.... 

и файл содержит несколько сотен строк (каждый из которых уникален для OldValue). Мне нужно обработать изменения по нескольким текстовым файлам в нескольких папках & вложенных папок. Мое лучшее предположение о количестве папок, файлов и строк текста - 15 папок, около 150 текстовых файлов в каждой папке, с примерно 65 000 строк текста в каждой папке (между 400-500 строк в текстовом файле).

Я сделаю 2 прохода по данным, если только не могу это сделать в одном. Первый проход - создать текстовый файл, который я буду использовать в качестве контрольного списка для просмотра моих изменений. Второй проход - это фактически внести изменения в файл. Кроме того, я хочу только изменить текстовые файлы, в которых происходит строка (не каждый файл).

Я использую следующий сценарий Powershell для просмотра файлов &, чтобы получить список необходимых изменений. Сценарий запускается, но выходит за рамки медленного. Я еще не работал над логикой замены, но я предполагаю, что это будет похоже на то, что у меня есть.

# replace a string in a file with powershell 
[reflection.assembly]::loadwithpartialname("Microsoft.VisualBasic") | Out-Null 

Function Search { 
    # Parameters $Path and $SearchString 
    param ([Parameter(Mandatory=$true, ValueFromPipeline = $true)][string]$Path, 
    [Parameter(Mandatory=$true)][string]$SearchString 
) 
    try { 
    #.NET FindInFiles Method to Look for file 

    [Microsoft.VisualBasic.FileIO.FileSystem]::GetFiles(
    $Path, 
    [Microsoft.VisualBasic.FileIO.SearchOption]::SearchAllSubDirectories, 
    $SearchString 
    ) 
    } catch { $_ } 

} 


if (Test-Path "C:\Work\ListofAllFilenamesToSearch.txt") { # if file exists 
    Remove-Item "C:\Work\ListofAllFilenamesToSearch.txt" 
    } 
if (Test-Path "C:\Work\FilesThatNeedToBeChanged.txt") { # if file exists 
    Remove-Item "C:\Work\FilesThatNeedToBeChanged.txt" 
    } 

$filefolder1 = "C:\TestFolder\WorkFiles" 
$ftype = "*.txt" 
$filenames1 = Search $filefolder1 $ftype 

$filenames1 | Out-File "C:\Work\ListofAllFilenamesToSearch.txt" -Width 2000 

if (Test-Path "C:\Work\FilesThatNeedToBeChanged.txt") { # if file exists 
    Remove-Item "C:\Work\FilesThatNeedToBeChanged.txt" 
    } 

(Get-Content "C:\Work\NumberXrefList.CSV" |where {$_.readcount -gt 1}) | foreach{ 
    $OldFieldValue, $NewFieldValue = $_.Split("|") 
    $filenamelist = (Get-Content "C:\Work\ListofAllFilenamesToSearch.txt" -ReadCount 5) #| 
    foreach ($j in $filenamelist) { 
    #$testvar = (Get-Content $j) 
    #$testvar = (Get-Content $j -ReadCount 100) 
    $testvar = (Get-Content $j -Delimiter "\n") 
      Foreach ($i in $testvar) 
      { 
      if ($i -imatch $OldFieldValue) { 
       $j + "|" + $OldFieldValue + "|" + $NewFieldValue | Out-File "C:\Work\FilesThatNeedToBeChanged.txt" -Width 2000 -Append 
       } 
      } 
    } 
} 

$FileFolder = (Get-Content "C:\Work\FilesThatNeedToBeChanged.txt" -ReadCount 5) 

Get-ChildItem $FileFolder -Recurse | 
select -ExpandProperty fullname | 
foreach { 
    if (Select-String -Path $_ -SimpleMatch $OldFieldValue -Debug -Quiet) { 
     (Get-Content $_) | 
     ForEach-Object {$_ -replace $OldFieldValue, $NewFieldValue }| 
     Set-Content $_ -WhatIf 
    } 
} 

В приведенном выше коде, я попытался несколько вещей с Get-Content - default, с -ReadCount и -Delimiter - в попытке избежать отказа от ошибки памяти.

Единственное, что у меня есть, - это длина старого файла замены строк &. Есть ли способ сделать это в Powershell? Есть ли лучший вариант/решение? Я запускаю Windows 7, Powershell версии 3.0.

ответ

1

Основная проблема заключается в том, что вы читаете файл снова и снова, чтобы изменить каждое из условий. Вы должны инвертировать цикл замены терминов и цикл файлов. Кроме того, предварительно загрузите csv. Что-то вроде:

$filefolder1 = "C:\TestFolder\WorkFiles" 
$ftype = "*.txt" 
$filenames = gci -Path $filefolder1 -Filter $ftype -Recurse 

$replaceValues = Import-Csv -Path "C:\Work\NumberXrefList.CSV" 

foreach ($file in $filenames) { 
    $contents = Get-Content -Path $file 

    foreach ($replaceValue in $replaceValues) {  
     $contents = $contents -replace $replaceValue.OldValue, $replaceValue.NewValue 
    } 

    Copy-Item $file "$file.old" 
    Set-Content -Path $file -Value $contents 
}