So ersetzen Sie mehrere Zeichenfolgen in einer Datei mit PowerShell

106

Ich schreibe ein Skript zum Anpassen einer Konfigurationsdatei. Ich möchte mehrere Instanzen von Zeichenfolgen in dieser Datei ersetzen und habe versucht, die Aufgabe mit PowerShell auszuführen.

Es funktioniert gut für ein einzelnes Ersetzen, aber das Ersetzen mehrerer Ersetzungen ist sehr langsam, da jedes Mal die gesamte Datei erneut analysiert werden muss und diese Datei sehr groß ist. Das Skript sieht folgendermaßen aus:

$original_file = 'path\filename.abc'
$destination_file =  'path\filename.abc.new'
(Get-Content $original_file) | Foreach-Object {
    $_ -replace 'something1', 'something1new'
    } | Set-Content $destination_file

Ich möchte so etwas, aber ich weiß nicht, wie ich es schreiben soll:

$original_file = 'path\filename.abc'
$destination_file =  'path\filename.abc.new'
(Get-Content $original_file) | Foreach-Object {
    $_ -replace 'something1', 'something1aa'
    $_ -replace 'something2', 'something2bb'
    $_ -replace 'something3', 'something3cc'
    $_ -replace 'something4', 'something4dd'
    $_ -replace 'something5', 'something5dsf'
    $_ -replace 'something6', 'something6dfsfds'
    } | Set-Content $destination_file
Ivo Bosticky
quelle

Antworten:

167

Eine Möglichkeit besteht darin, die -replaceOperationen miteinander zu verketten. Das `am Ende jeder Zeile wird aus der neuen Zeile entfernt, sodass PowerShell den Ausdruck in der nächsten Zeile weiter analysiert:

$original_file = 'path\filename.abc'
$destination_file =  'path\filename.abc.new'
(Get-Content $original_file) | Foreach-Object {
    $_ -replace 'something1', 'something1aa' `
       -replace 'something2', 'something2bb' `
       -replace 'something3', 'something3cc' `
       -replace 'something4', 'something4dd' `
       -replace 'something5', 'something5dsf' `
       -replace 'something6', 'something6dfsfds'
    } | Set-Content $destination_file

Eine andere Möglichkeit wäre, eine Zwischenvariable zuzuweisen:

$x = $_ -replace 'something1', 'something1aa'
$x = $x -replace 'something2', 'something2bb'
...
$x
Dahlbyk
quelle
Kann $ original_file == $ destination_file? Wie in Ich ändere die gleiche Datei wie meine Quelle?
Cquadrini
Aufgrund der Art und Weise, wie PowerShell-Cmdlets ihre Eingabe / Ausgabe streamen, glaube ich nicht, dass es funktionieren würde, in dieselbe Datei in derselben Pipeline zu schreiben. Sie könnten jedoch so etwas tun $c = Get-Content $original_file; $c | ... | Set-Content $original_file.
Dahlbyk
Haben Sie Probleme mit der Dateicodierung mit Set-Content , die nicht die ursprüngliche Codierung enthält? Zum Beispiel UTF-8- oder ANSI-Codierungen.
Kiquenet
1
Ja, PowerShell ist ... so wenig hilfreich. Sie müssen die Codierung selbst erkennen, z. B. github.com/dahlbyk/posh-git/blob/…
dahlbyk
24

Damit der Beitrag von George Howarth mit mehr als einem Ersatz ordnungsgemäß funktioniert, müssen Sie die Unterbrechung entfernen, die Ausgabe einer Variablen ($ line) zuweisen und dann die Variable ausgeben:

$lookupTable = @{
    'something1' = 'something1aa'
    'something2' = 'something2bb'
    'something3' = 'something3cc'
    'something4' = 'something4dd'
    'something5' = 'something5dsf'
    'something6' = 'something6dfsfds'
}

$original_file = 'path\filename.abc'
$destination_file =  'path\filename.abc.new'

Get-Content -Path $original_file | ForEach-Object {
    $line = $_

    $lookupTable.GetEnumerator() | ForEach-Object {
        if ($line -match $_.Key)
        {
            $line = $line -replace $_.Key, $_.Value
        }
    }
   $line
} | Set-Content -Path $destination_file
TroyBramley
quelle
Dies ist bei weitem der beste Ansatz, den ich bisher gesehen habe. Das einzige Problem ist, dass ich zuerst den gesamten Dateiinhalt einer Variablen vorlesen musste, um dieselben Quell- / Zieldateipfade zu verwenden.
Angularsen
Dies scheint die beste Antwort zu sein, obwohl ich ein seltsames Verhalten gesehen habe, bei dem es falsch übereinstimmt. Das heißt, wenn Sie eine Hash-Tabelle mit Hex-Werten als Zeichenfolgen (0x0, 0x1, 0x100, 0x10000) haben und 0x10000 mit 0x1 übereinstimmen.
Lorek
13

Mit Version 3 von PowerShell können Sie die Ersetzungsaufrufe miteinander verketten:

 (Get-Content $sourceFile) | ForEach-Object {
    $_.replace('something1', 'something1').replace('somethingElse1', 'somethingElse2')
 } | Set-Content $destinationFile
Ian Robertson
quelle
Funktioniert gut + fließendes Aroma
hdoghmen
10

Angenommen, Sie können nur eine 'something1'oder 'something2'usw. pro Zeile haben, können Sie eine Nachschlagetabelle verwenden:

$lookupTable = @{
    'something1' = 'something1aa'
    'something2' = 'something2bb'
    'something3' = 'something3cc'
    'something4' = 'something4dd'
    'something5' = 'something5dsf'
    'something6' = 'something6dfsfds'
}

$original_file = 'path\filename.abc'
$destination_file =  'path\filename.abc.new'

Get-Content -Path $original_file | ForEach-Object {
    $line = $_

    $lookupTable.GetEnumerator() | ForEach-Object {
        if ($line -match $_.Key)
        {
            $line -replace $_.Key, $_.Value
            break
        }
    }
} | Set-Content -Path $destination_file

Wenn Sie mehr als eine davon haben können, entfernen Sie einfach die breakin der ifAnweisung.

George Howarth
quelle
Ich sehe, dass TroyBramley kurz vor der letzten Zeile $ line hinzugefügt hat, um eine Zeile zu schreiben, die keine Änderungen enthält. In Ordnung. In meinem Fall habe ich nur jede Zeile geändert, die ersetzt werden muss.
Cliffclof
8

Eine dritte Option für einen Einzeiler mit Pipeline besteht darin, die Ersetzungen zu verschachteln:

PS> ("ABC" -replace "B","C") -replace "C","D"
ADD

Und:

PS> ("ABC" -replace "C","D") -replace "B","C"
ACD

Dadurch bleibt die Ausführungsreihenfolge erhalten, sie ist leicht zu lesen und passt problemlos in eine Pipeline. Ich bevorzuge Klammern zur expliziten Kontrolle, Selbstdokumentation usw. Es funktioniert ohne sie, aber wie weit vertrauen Sie dem?

-Replace ist ein Vergleichsoperator, der ein Objekt akzeptiert und ein vermutlich geändertes Objekt zurückgibt. Aus diesem Grund können Sie sie wie oben gezeigt stapeln oder verschachteln.

Bitte sehen Sie:

help about_operators

quelle