Comment remplacer plusieurs chaînes dans un fichier à l'aide de PowerShell

107

J'écris un script pour personnaliser un fichier de configuration. Je souhaite remplacer plusieurs instances de chaînes dans ce fichier et j'ai essayé d'utiliser PowerShell pour faire le travail.

Cela fonctionne bien pour un seul remplacement, mais effectuer plusieurs remplacements est très lent car à chaque fois, il doit analyser à nouveau le fichier entier, et ce fichier est très volumineux. Le script ressemble à ceci:

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

Je veux quelque chose comme ça, mais je ne sais pas comment l'écrire:

$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
la source

Réponses:

167

Une option consiste à enchaîner les -replaceopérations. Le `à la fin de chaque ligne échappe la nouvelle ligne, ce qui oblige PowerShell à continuer à analyser l'expression sur la ligne suivante:

$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

Une autre option serait d'attribuer une variable intermédiaire:

$x = $_ -replace 'something1', 'something1aa'
$x = $x -replace 'something2', 'something2bb'
...
$x
Dahlbyk
la source
$ Original_file == $ fichier_destination? Comme dans je modifie le même fichier que ma source?
cquadrini
En raison de la façon dont les applets de commande PowerShell diffusent leur entrée / sortie, je ne pense pas que cela fonctionnerait d'écrire dans le même fichier dans le même pipeline. Cependant, vous pouvez faire quelque chose comme $c = Get-Content $original_file; $c | ... | Set-Content $original_file.
dahlbyk
Avez-vous des problèmes avec le codage de fichiers à l'aide de Set-Content qui ne conserve pas le codage d'origine? Encodages UTF-8 ou ANSI par exemple.
Kiquenet
1
Ouais PowerShell est ... inutile comme ça. Vous devez détecter vous-même l'encodage, par exemple github.com/dahlbyk/posh-git/blob/…
dahlbyk
24

Pour que la publication de George Howarth fonctionne correctement avec plus d'un remplacement, vous devez supprimer la rupture, affecter la sortie à une variable ($ line), puis afficher la variable:

$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
la source
C'est de loin la meilleure approche que j'ai vue jusqu'à présent. Le seul problème est que je devais d'abord lire tout le contenu du fichier dans une variable afin d'utiliser les mêmes chemins de fichier source / destination.
angularsen
cela ressemble à la meilleure réponse, même si j'ai vu un comportement étrange avec une correspondance incorrecte. c'est-à-dire dans le cas où vous avez une table de hachage avec des valeurs hexadécimales sous forme de chaînes (0x0, 0x1, 0x100, 0x10000) et 0x10000 correspondra à 0x1.
Lorek
13

Avec la version 3 de PowerShell, vous pouvez enchaîner les appels de remplacement:

 (Get-Content $sourceFile) | ForEach-Object {
    $_.replace('something1', 'something1').replace('somethingElse1', 'somethingElse2')
 } | Set-Content $destinationFile
Ian Robertson
la source
Fonctionne bien + saveur fluide
hdoghmen
10

En supposant que vous ne pouvez en avoir qu'un 'something1'ou 'something2', etc. par ligne, vous pouvez utiliser une table de recherche:

$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

Si vous pouvez en avoir plusieurs, supprimez simplement le breakdans l' ifinstruction.

George Howarth
la source
Je vois que TroyBramley a ajouté $ line juste avant la dernière ligne pour écrire n'importe quelle ligne qui ne comportait aucun changement. D'accord. Dans mon cas, je n'ai changé que toutes les lignes nécessitant des remplacements.
cliffclof
8

Une troisième option, pour un one-liner pipelined, consiste à imbriquer les -replaces:

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

Et:

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

Cela préserve l'ordre d'exécution, est facile à lire et s'intègre parfaitement dans un pipeline. Je préfère utiliser des parenthèses pour le contrôle explicite, l'auto-documentation, etc. Cela fonctionne sans elles, mais dans quelle mesure faites-vous confiance?

-Replace est un opérateur de comparaison, qui accepte un objet et renvoie un objet vraisemblablement modifié. C'est pourquoi vous pouvez les empiler ou les imbriquer comme indiqué ci-dessus.

S'il te plait regarde:

help about_operators

la source