web-dev-qa-db-de.com

Wie kann ich jedes Vorkommen eines Strings in einer Datei mit PowerShell ersetzen?

Mit PowerShell möchte ich alle genauen Vorkommen von [MYID] in einer bestimmten Datei durch MyValue ersetzen. Was ist der einfachste Weg, dies zu tun?

234
amateur

Verwendung (Version V3):

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

Oder für 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

Beachten Sie die Klammern um (Get-Content file.txt):

Ohne die Klammer wird der Inhalt Zeile für Zeile gelesen und fließt die Pipeline herunter, bis er out-file oder set-content erreicht, der versucht, in dieselbe Datei zu schreiben, aber er ist bereits mit get-content geöffnet und Sie erhalten ein Fehler. Die Klammer bewirkt, dass der Vorgang des Inhaltslesens einmal ausgeführt wird (Öffnen, Lesen und Schließen). Nur dann, wenn alle Zeilen gelesen wurden, werden sie einzeln geleitet, und wenn sie den letzten Befehl in der Pipeline erreichen, können sie in die Datei geschrieben werden. Es ist dasselbe wie $ content = content; $ content | woher ...

81
Shay Levy

Ich bevorzuge die Verwendung der File-Klasse von .NET und ihrer statischen Methoden, wie im folgenden Beispiel gezeigt.

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

Dies hat den Vorteil, dass mit einem einzelnen String anstatt mit einem String-Array wie mit Get-Content gearbeitet wird. Die Methoden kümmern sich auch um die Codierung der Datei (UTF-8 BOM usw.), ohne dass Sie sich die meiste Zeit darum kümmern müssen.

Die Methoden vermischen auch nicht die Zeilenenden (möglicherweise verwendete Unix-Zeilenenden) im Gegensatz zu einem Algorithmus, der Get-Content verwendet und bis Set-Content weiterleitet.

Also für mich: Weniger Dinge, die im Laufe der Jahre brechen könnten.

Wenn Sie .NET-Klassen verwenden, ist es wenig bekannt, dass Sie bei Eingabe von "[System.IO.File] ::" im PowerShell-Fenster die Taste drücken können Tab Taste, um die dortigen Methoden zu durchlaufen.

72
rominator007

Die obige Datei wird nur für "One File" ausgeführt, Sie können diese jedoch auch für mehrere Dateien in Ihrem Ordner ausführen:

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

Sie könnten so etwas versuchen: 

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

Ich verwende dies, aber bei großen Textdateien ist es langsam.

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

Wenn Sie Zeichenketten in großen Textdateien ersetzen möchten und Geschwindigkeit ein Problem ist, lesen Sie die Informationen unter System.IO.StreamReader und 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()
   }
}

(Der obige Code wurde nicht getestet.)

Es gibt wahrscheinlich eine elegantere Methode, StreamReader und StreamWriter zum Ersetzen von Text in einem Dokument zu verwenden. Dies sollte jedoch einen guten Ausgangspunkt bieten.

Ich fand einen wenig bekannten, aber erstaunlich coolen Weg, dies mit Payettes Windows Powershell in Action zu tun. Sie können Dateien wie Variablen referenzieren, ähnlich wie $ env: path, aber Sie müssen die geschweiften Klammern hinzufügen.

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

Dies funktionierte für mich mit dem aktuellen Arbeitsverzeichnis in PowerShell. Sie müssen die FullName-Eigenschaft verwenden. In PowerShell Version 5 wird dies nicht funktionieren. Ich musste die .NET Framework-Zielversion in ALLEN CSPROJ-Dateien ändern.

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

Etwas alt und anders, da ich in allen Instanzen eines bestimmten Dateinamens eine bestimmte Zeile ändern musste.

Außerdem gab Set-Content keine konsistenten Ergebnisse zurück, daher musste ich zu Out-File zurückkehren.

Code unten:


$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
}

Dies hat bei dieser PowerShell-Version für mich am besten funktioniert:

Major.Minor.Build.Revision

5.1.16299.98

0
Kalin Tashev

Gutschrift an @ rominator007

Ich habe es in eine Funktion eingepackt (weil Sie es vielleicht wieder verwenden möchten)

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

HINWEIS: Dies ist NICHT case sensitive !!!!!

Siehe diesen Beitrag: String.Replace ignoriert den Fall

0
Kolob Canyon