À l'aide de PowerShell, je souhaite remplacer toutes les occurrences exactes de [MYID]
dans un fichier donné par MyValue
. Quel est le moyen le plus simple de le faire?
Utilisation (version V3):
(Get-Content c:\temp\test.txt).replace('[MYID]', 'MyValue') | Set-Content c:\temp\test.txt
Ou pour V2:
(Get-Content c:\temp\test.txt) -replace '\[MYID\]', 'MyValue' | Set-Content c:\temp\test.txt
(Get-Content file.txt) |
Foreach-Object {$_ -replace '\[MYID\]','MyValue'} |
Out-File file.txt
Notez que les parenthèses autour de (Get-Content file.txt)
sont obligatoires:
Sans la parenthèse, le contenu est lu, ligne par ligne, et passe par le pipeline jusqu'à ce qu'il atteigne le contenu sortant ou le contenu défini, ce qui tente d'écrire dans le même fichier, mais il est déjà ouvert par get-content et vous obtenez une erreur. La parenthèse provoque l'opération de lecture du contenu à effectuer une fois (ouvrir, lire et fermer). Ce n'est qu'alors que toutes les lignes ont été lues, elles sont redirigées une à la fois et lorsqu'elles atteignent la dernière commande du pipeline, elles peuvent être écrites dans le fichier. C'est la même chose que $ content = content; $ content | où ...
Je préfère utiliser la classe File de .NET et ses méthodes statiques, comme illustré dans l'exemple suivant.
$content = [System.IO.File]::ReadAllText("c:\bla.txt").Replace("[MYID]","MyValue")
[System.IO.File]::WriteAllText("c:\bla.txt", $content)
Cela présente l'avantage de travailler avec une seule chaîne au lieu d'un tableau-chaîne comme avec Get-Content . Les méthodes s’occupent également de l’encodage du fichier (UTF-8 BOM , etc.) sans que vous ayez à vous en occuper la plupart du temps.
De plus, les méthodes ne gâchent pas les fins de lignes (les fins de lignes Unix pouvant être utilisées), contrairement à un algorithme utilisant Get-Content et la tuyauterie jusqu'à Set-Content .
Donc pour moi: moins de choses qui pourraient se casser au fil des ans.
Une chose peu connue lors de l’utilisation des classes .NET est que lorsque vous avez tapé "[System.IO.File] ::" dans la fenêtre PowerShell, vous pouvez appuyer sur le bouton Tab clé pour parcourir les méthodes là-bas.
Celui ci-dessus ne fonctionne que pour "Un fichier", mais vous pouvez également l'exécuter pour plusieurs fichiers de votre dossier:
Get-ChildItem 'C:yourfile*.xml' -Recurse | ForEach {
(Get-Content $_ | ForEach { $_ -replace '[MYID]', 'MyValue' }) |
Set-Content $_
}
Vous pouvez essayer quelque chose comme ça:
$path = "C:\testFile.txt"
$Word = "searchword"
$replacement = "ReplacementText"
$text = get-content $path
$newText = $text -replace $Word,$replacement
$newText > $path
C’est ce que j’utilise, mais c’est lent sur les gros fichiers texte.
get-content $pathToFile | % { $_ -replace $stringToReplace, $replaceWith } | set-content $pathToFile
Si vous envisagez de remplacer des chaînes dans des fichiers texte volumineux et que la vitesse est un problème, envisagez d'utiliser System.IO.StreamReader et 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()
}
}
(Le code ci-dessus n'a pas été testé.)
Il existe probablement une manière plus élégante d’utiliser StreamReader et StreamWriter pour remplacer du texte dans un document, mais cela devrait vous donner un bon point de départ.
J'ai trouvé un moyen peu connu mais étonnamment cool de le faire grâce à Windows Powershell in Action . Vous pouvez référencer des fichiers comme des variables, similaires à $ env: path, mais vous devez ajouter des accolades.
${c:file.txt} = ${c:file.txt} -replace 'oldvalue','newvalue'
Cela a fonctionné pour moi en utilisant le répertoire de travail actuel dans PowerShell. Vous devez utiliser la propriété FullName
, sinon cela ne fonctionnera pas dans PowerShell version 5. Je devais changer la version du framework .NET cible dans TOUS mes fichiers CSPROJ
.
gci -Recurse -Filter *.csproj |
% { (get-content "$($_.FullName)")
.Replace('<TargetFramework>net47</TargetFramework>', '<TargetFramework>net462</TargetFramework>') |
Set-Content "$($_.FullName)"}
Un peu vieux et différent, car j'avais besoin de changer une certaine ligne dans toutes les instances d'un nom de fichier particulier.
De plus, Set-Content
ne renvoyait pas de résultats cohérents, j'ai donc dû recourir à Out-File
.
Code ci-dessous:
$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
}
C'est ce qui a le mieux fonctionné pour moi sur cette version de PowerShell:
Major.Minor.Build.Revision
5.1.16299.98
Crédit à @ rominator007
Je l'ai enveloppé dans une fonction (parce que vous voudrez peut-être l'utiliser à nouveau)
function Replace-AllStringsInFile($SearchString,$ReplaceString,$FullPathToFile)
{
$content = [System.IO.File]::ReadAllText("$FullPathToFile").Replace("$SearchString","$ReplaceString")
[System.IO.File]::WriteAllText("$FullPathToFile", $content)
}
NOTE: Ceci n'est PAS sensible à la casse !!!!!
Voir ce message: String.Replace ignoring case