it-roy-ru.com

Как я могу заменить каждое вхождение строки в файле с PowerShell?

Используя PowerShell, я хочу заменить все точные вхождения [MYID] в данном файле на MyValue. Какой самый простой способ сделать это?

234
amateur

Используйте (версия V3):

(Get-Content c:\temp\test.txt).replace('[MYID]', 'MyValue') | Set-Content c:\temp\test.txt

Или для V2:

(Get-Content c:\temp\test.txt) -replace '\[MYID\]', 'MyValue' | Set-Content c:\temp\test.txt
355
Loïc MICHEL
(Get-Content file.txt) | 
Foreach-Object {$_ -replace '\[MYID\]','MyValue'}  | 
Out-File file.txt

Обратите внимание, что скобки вокруг (Get-Content file.txt) обязательны:

Без круглых скобок содержимое читается по одной строке за раз и направляется вниз по конвейеру, пока не достигнет out-file или set-content, который пытается записать в тот же файл, но он уже открыт get-content, и вы получаете ошибка. Скобка заставляет операцию чтения содержимого выполняться один раз (открыть, прочитать и закрыть). Только тогда, когда все строки были прочитаны, они передаются по очереди, и когда они достигают последней команды в конвейере, они могут быть записаны в файл. Это то же самое, что $ content = content; $ контент | где ...

81
Shay Levy

Я предпочитаю использовать File-класс .NET и его статические методы, как показано в следующем примере.

$content = [System.IO.File]::ReadAllText("c:\bla.txt").Replace("[MYID]","MyValue")
[System.IO.File]::WriteAllText("c:\bla.txt", $content)

Это дает преимущество работы с одной строкой вместо массива строк, как с Get-Content . Методы также заботятся о кодировке файла (UTF-8 BOM и т.д.), При этом вам не нужно заботиться большую часть времени.

Кроме того, методы не портят окончания строк (которые могут использоваться в конце Unix-строк), в отличие от алгоритма, использующего Get-Content и передающего до Set-Content .

Так что для меня: меньше вещей, которые могут сломаться за эти годы.

Малоизвестная вещь при использовании классов .NET заключается в том, что когда вы ввели "[System.IO.File] ::" в окне PowerShell, вы можете нажать Tab ключ, чтобы пройти через методы там.

72
rominator007

Вышеприведенный пример работает только для «одного файла», но вы также можете запустить его для нескольких файлов в вашей папке:

Get-ChildItem 'C:yourfile*.xml' -Recurse | ForEach {
     (Get-Content $_ | ForEach  { $_ -replace '[MYID]', 'MyValue' }) |
     Set-Content $_
}
18
John V Hobbs Jr

Вы можете попробовать что-то вроде этого: 

$path = "C:\testFile.txt"
$Word = "searchword"
$replacement = "ReplacementText"
$text = get-content $path 
$newText = $text -replace $Word,$replacement
$newText > $path
9
Richard

Это то, что я использую, но это медленно на больших текстовых файлах.

get-content $pathToFile | % { $_ -replace $stringToReplace, $replaceWith } | set-content $pathToFile

Если вы собираетесь заменять строки в больших текстовых файлах, а скорость - проблема, посмотрите на использование System.IO.StreamReader и System.IO.StreamWriter .

try
{
   $reader = [System.IO.StreamReader] $pathToFile
   $data = $reader.ReadToEnd()
   $reader.close()
}
finally
{
   if ($reader -ne $null)
   {
       $reader.dispose()
   }
}

$data = $data -replace $stringToReplace, $replaceWith

try
{
   $writer = [System.IO.StreamWriter] $pathToFile
   $writer.write($data)
   $writer.close()
}
finally
{
   if ($writer -ne $null)
   {
       $writer.dispose()
   }
}

(Код выше не был проверен.)

Вероятно, есть более элегантный способ использовать StreamReader и StreamWriter для замены текста в документе, но это должно дать вам хорошую отправную точку.

7
Darrell Lloyd Harvey

Я нашел малоизвестный, но удивительно крутой способ сделать это из Windows Powershell в действии. Вы можете ссылаться на файлы как переменные, похожие на $ env: path, но вам нужно добавить фигурные скобки.

${c:file.txt} = ${c:file.txt} -replace 'oldvalue','newvalue'
0
js2010

Это работало для меня, используя текущий рабочий каталог в PowerShell. Вам нужно использовать свойство FullName, иначе оно не будет работать в PowerShell версии 5. Мне нужно было изменить целевую версию .NET framework во ВСЕХ моих файлах CSPROJ.

gci -Recurse -Filter *.csproj |
% { (get-content "$($_.FullName)")
.Replace('<TargetFramework>net47</TargetFramework>', '<TargetFramework>net462</TargetFramework>') |
 Set-Content "$($_.FullName)"}
0
SliverNinja - MSFT

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

Кроме того, Set-Content не возвращал согласованные результаты, поэтому мне пришлось прибегнуть к Out-File.

Код ниже:


$FileName =''
$OldLine = ''
$NewLine = ''
$Drives = Get-PSDrive -PSProvider FileSystem
foreach ($Drive in $Drives) {
    Push-Location $Drive.Root
        Get-ChildItem -Filter "$FileName" -Recurse | ForEach { 
            (Get-Content $_.FullName).Replace($OldLine, $NewLine) | Out-File $_.FullName
        }
    Pop-Location
}

Вот что лучше всего сработало для меня в этой версии PowerShell:

Major.Minor.Build.Revision

5.1.16299.98

0
Kalin Tashev

Кредит @ rominator007

Я обернул его в функцию (потому что вы можете использовать его снова)

function Replace-AllStringsInFile($SearchString,$ReplaceString,$FullPathToFile)
{
    $content = [System.IO.File]::ReadAllText("$FullPathToFile").Replace("$SearchString","$ReplaceString")
    [System.IO.File]::WriteAllText("$FullPathToFile", $content)
}

ПРИМЕЧАНИЕ: это НЕ чувствительно к регистру !!!!!

Смотрите этот пост: String.Заменить игнорирующий регистр

0
Kolob Canyon