117 votes

Comment remplacer plusieurs chaînes de caractères dans un fichier en utilisant PowerShell

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

Cela fonctionne bien pour un seul remplacement, mais faire plusieurs remplacements est très lent parce qu'à chaque fois il doit analyser à nouveau tout le fichier, et ce fichier est très grand. 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

178voto

dahlbyk Points 24897

Une option consiste à enchaîner les -replace opérations ensemble. Le site ` à la fin de chaque ligne échappe la nouvelle ligne, ce qui permet à PowerShell de 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

0 votes

Est-ce que $original_file == $destination_file ? Comme dans le cas où je modifie le même fichier que ma source ?

0 votes

En raison de la manière dont les cmdlets PowerShell diffusent leurs entrées/sorties, je ne pense pas qu'il soit possible d'écrire dans le même fichier dans le même pipeline. Cependant, vous pourriez faire quelque chose comme $c = Get-Content $original_file; $c | ... | Set-Content $original_file .

0 votes

Avez-vous des problèmes avec l'encodage des fichiers en utilisant Set-Content qui ne conserve pas l'encodage original ? Les encodages UTF-8 ou ANSI par exemple.

25voto

TroyB30 Points 186

Pour que l'article de George Howarth fonctionne correctement avec plus d'un remplacement, vous devez supprimer la rupture, attribuer la sortie à une variable ($line), puis sortir 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

0 votes

C'est de loin la meilleure approche que j'ai vue jusqu'à présent. Le seul problème est que j'ai dû d'abord lire le contenu entier du fichier dans une variable afin d'utiliser les mêmes chemins de fichier source/destination.

0 votes

Cela semble être la meilleure réponse, bien que j'ai vu un comportement bizarre avec des correspondances incorrectes. Par exemple, dans le cas où vous avez une table de hachage avec des valeurs hexagonales comme chaînes (0x0, 0x1, 0x100, 0x10000) et 0x10000 correspondra à 0x1.

15voto

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

0 votes

Fonctionne bien + saveur fluide

0 votes

Tant que vous n'avez pas besoin de RegEx

0 votes

Où la question mentionne-t-elle les regex ?

10voto

George Howarth Points 1364

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

$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 plus d'un, il suffit de supprimer l'élément break dans le if déclaration.

0 votes

Je vois que TroyBramley a ajouté $line juste avant la dernière ligne pour écrire toute ligne qui n'a pas été modifiée. Ok. Dans mon cas, j'ai seulement changé toutes les lignes qui avaient besoin d'être remplacées.

10voto

Haakon Dahl Points 1

Une troisième option, pour une ligne unique pipelinée, 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 les parenthèses pour le contrôle explicite, l'auto-documentation, etc. Cela fonctionne sans elles, mais jusqu'à quel point faites-vous confiance à cela ?

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

Veuillez voir :

help about_operators

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X