Comment puis-je remplacer chaque occurrence d'une chaîne dans un fichier par PowerShell?
en utilisant PowerShell, je veux remplacer toutes les occurrences exactes de [MYID]
dans un fichier donné par MyValue
. Quelle est la meilleure façon de le faire?
11 réponses
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
noter les parenthèses autour de (Get-Content file.txt)
est requis:
sans la parenthèse le contenu est lu, une ligne à la fois, et coule le long du pipeline jusqu'à ce qu'il atteigne out-file ou set-content, 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 fait que l'opération de lecture de contenu est effectuée une fois (ouvert, lu et fermé). Seulement alors, quand toutes les lignes ont été lire, ils arrivent un à un, et quand ils atteignent la dernière commande dans le pipeline ils peuvent être écrites dans le fichier. C'est le même que $contenu=contenu; $contenu | où ...
je préfère utiliser la classe File-class de .NET et ses méthodes statiques comme dans l'exemple suivant.
$content = [System.IO.File]::ReadAllText("c:\bla.txt").Replace("[MYID]","MyValue")
[System.IO.File]::WriteAllText("c:\bla.txt", $content)
ceci a l'avantage de travailler avec une seule chaîne de caractères au lieu d'une chaîne de caractères comme avec Get-Content . Les méthodes prennent également soin de l'encodage du fichier (UTF-8 BOM , etc.) sans avoir à prendre soin, la plupart du temps.
aussi les méthodes ne gâchent pas la ligne les terminaisons (terminaisons de ligne Unix qui pourraient être utilisées) en contraste avec un algorithme utilisant Get-Content et piping jusqu'à Set-Content .
donc pour moi: moins de choses qui pourraient casser au fil des ans.
une chose peu connue lorsque vous utilisez les classes .NET est que lorsque vous avez tapé" [System.IO.Fichier]:: "dans la fenêtre PowerShell, vous pouvez appuyer sur la touche Tab pour passer à travers les méthodes.
celui ci-dessus ne s'exécute que pour "un seul fichier", mais vous pouvez également l'Exécuter pour plusieurs fichiers dans votre dossier:
Get-ChildItem 'C:yourfile*.xml' -Recurse | ForEach {
(Get-Content $_ | ForEach { $_ -replace '[MYID]', 'MyValue' }) |
Set-Content $_
}
vous pourriez essayer quelque chose comme ceci:
$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 il est lent sur de gros fichiers texte.
get-content $pathToFile | % { $_ -replace $stringToReplace, $replaceWith } | set-content $pathToFile
si vous remplacez des chaînes de caractères dans des fichiers texte volumineux et que la vitesse est un problème, cherchez à utiliser le système .IO.StreamReader and 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 y a probablement une façon 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.
cela a fonctionné pour moi en utilisant le répertoire de travail courant dans PowerShell. Vous devez utiliser la propriété FullName
, sinon elle ne fonctionnera pas dans la version 5 de PowerShell. J'avais besoin de changer la version cible .net framework dans tous mes fichiers CSPROJ
.
gci -Recurse -Filter *.csproj |
% { (get-content "$($_.FullName)")
.Replace('<TargetFramework>net47</TargetFramework>', '<TargetFramework>net462</TargetFramework>') |
Set-Content "$($_.FullName)"}
après avoir trop cherché, j'ai trouvé la ligne la plus simple pour faire ceci sans changer le encodage est:
Get-Content path/to/file.ext | out-file -encoding ASCII targetFile.ext
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.
aussi, Set-Content
ne retournait pas des résultats cohérents, donc j'ai 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 fonctionné le mieux pour moi sur cette version PowerShell:
Majeur.Mineur.Construire.Révision
5.1.16299.98
crédit à @rominator007
j'ai enveloppé dans une fonction (car vous pouvez 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: Ce n'est pas sensible à la casse!!!!!
Voir ce post: de la Chaîne.Remplacer ignorant la casse
petite correction pour la commande Set-Content. Si la chaîne recherchée n'est pas trouvée, la commande Set-Content
vide le fichier cible.
vous pouvez d'abord vérifier si la chaîne que vous recherchez existe ou non. Si non, elle ne remplacera pas quoi que ce soit.
If (select-string -path "c:\Windows\System32\drivers\etc\hosts" -pattern "String to look for") `
{(Get-Content c:\Windows\System32\drivers\etc\hosts).replace('String to look for', 'String to replace with') | Set-Content c:\Windows\System32\drivers\etc\hosts}
Else{"Nothing happened"}