Powershell: замена строк с помощью хэш-таблицы



Итак, я создал хэш-таблицу с именами, которые нужно заменить, и ключами, которые нужно заменить, вот так:



$r = @{
"dog" = "canine";
"cat" = "feline";
"eric" = "eric cartman"
}


Что мне делать дальше? Я пробовал это:



(Get-Content C:scriptstest.txt) | Foreach-Object {
foreach ( $e in $r.GetEnumerator() ) {
$_ -replace $e.Name, $e.Value
}
} | Set-Content C:scriptstest.txt.out


Но он не работает вообще, он просто пишет каждую строку три раза, ничего не заменяя.

Правка: содержит тест.txt:



dog
cat
eric


Тест.формат txt.выход:



dog
dog
dog
cat
cat
cat
eric
eric
eric
747   3  

3 ответов:

Вот один из способов сделать это:

$file = Get-Content C:\scripts\test.txt
foreach ($e in $r) {
  $file = $file -replace $e.Name, $e.Value
}
Set-Content -Path C:\scripts\test.txt.out -Value $file

Причина, по которой вы видели каждую строку три раза, заключается во вложенном цикле foreach. Операция замены выполнялась один раз для каждой записи хэш-таблицы для каждой строки в файле. Это не изменяет исходный файл, но по умолчанию он выводит результат замены (даже если ничего не изменилось).

Вы можете получить желаемую функциональность, сначала прочитав файл в переменную, а затем используя циклическую замену, чтобы обновить эту переменную. Вам также не нужен отдельный цикл foreach для содержимого файла; замена может выполняться против полного текста за один проход на запись hashtable.

В зависимости от вашего файла и хэш-таблицы, вы можете рассмотреть различные варианты оптимизации:

  1. Вы можете построить регулярное выражение из коллекции ключей hashtable следующим образом:

    $regexes = $r.keys | foreach {[System.Text.RegularExpressions.Regex]::Escape($_)}
    $regex = [regex]($r.Keys -join '|')    
    

    При этом вы не будете повторять каждый ключ, но теперь вам нужно знать, какой ключ вы выбрали, чтобы получить замену. С другой стороны, это может быть быстрее, чтобы сделать замену строки вместо замены регулярных выражений (или что-то более сложное, как строка split и join процесс).

  2. В Powershell можно вызвать функцию .NET Regex::Replace:

    String Replace (строковый ввод, System.Текст.Регулярные депрессии.MatchEvaluator evaluator)

    Вызывая этот метод, вы можете определить MatchEvaluator с помощью скриптового блока следующим образом:

    $callback = { $r[$args[0].Value] }
    

    В скриптовом блоке $args[0] является System.Text.RegularExpressions.Match, поэтому его свойство Value можно использовать для индексирования в хеш-таблицу $r.

  3. Get-Content возвращает массив строк, который подходит для Оператор -replace, но также подразумевает выполнение дополнительного цикла. [System.IO.File]::ReadAllText вместо этого вернет одну строку, поэтому регулярное выражение должно быть проанализировано только один раз.

    $file = [System.IO.File]::ReadAllText("C:\scripts\test.txt")
    
  4. Если вы использовали Get-Content, то для использования $regex.Replace (вместо -replace) вам понадобится цикл:

    $file = $file | % { $regex.Replace($_, $callback) }
    

    Поскольку я не являюсь, я могу использовать один вызов замены:

    $file = $regex.Replace($file, $callback)
    

Таким образом, полный сценарий:

$r = @{
    "dog" = "canine";
    "cat" = "feline";
    "eric" = "eric cartman"
}


$regexes = $r.keys | foreach {[System.Text.RegularExpressions.Regex]::Escape($_)}
$regex = [regex]($regexes -join '|')

$callback = { $r[$args[0].Value] }

$file = [System.IO.File]::ReadAllText("C:\scripts\test.txt")
$file = $regex.Replace($file, $callback)
Set-Content -Path C:\scripts\test.txt.out -Value $file

У меня получилось так

foreach ($i in $HashTable.Keys) {
  $myString = $myString -replace $i, $HashTable[$i]
}

Comments

    Ничего не найдено.