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
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.
В зависимости от вашего файла и хэш-таблицы, вы можете рассмотреть различные варианты оптимизации:
Вы можете построить регулярное выражение из коллекции ключей hashtable следующим образом:
$regexes = $r.keys | foreach {[System.Text.RegularExpressions.Regex]::Escape($_)} $regex = [regex]($r.Keys -join '|')При этом вы не будете повторять каждый ключ, но теперь вам нужно знать, какой ключ вы выбрали, чтобы получить замену. С другой стороны, это может быть быстрее, чтобы сделать замену строки вместо замены регулярных выражений (или что-то более сложное, как строка split и join процесс).
В Powershell можно вызвать функцию .NET
Regex::Replace:String Replace (строковый ввод, System.Текст.Регулярные депрессии.MatchEvaluator evaluator)
Вызывая этот метод, вы можете определить
MatchEvaluatorс помощью скриптового блока следующим образом:$callback = { $r[$args[0].Value] }В скриптовом блоке
$args[0]являетсяSystem.Text.RegularExpressions.Match, поэтому его свойствоValueможно использовать для индексирования в хеш-таблицу$r.
Get-Contentвозвращает массив строк, который подходит для Оператор-replace, но также подразумевает выполнение дополнительного цикла.[System.IO.File]::ReadAllTextвместо этого вернет одну строку, поэтому регулярное выражение должно быть проанализировано только один раз.$file = [System.IO.File]::ReadAllText("C:\scripts\test.txt")Если вы использовали
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