Como posso replace cada ocorrência de uma String em um arquivo com o PowerShell?

Usando o PowerShell, quero replace todas as ocorrências exatas de [MYID] em um determinado arquivo com MyValue . Qual é a maneira mais fácil de fazer isso?

Use (versão V3):

 (Get-Content c:\temp\test.txt).replace('[MYID]', 'MyValue') | Set-Content c:\temp\test.txt 

Ou para 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 

Observe os parênteses ao redor (Get-Content file.txt) é necessário:

Sem o parêntese, o conteúdo é lido, uma linha por vez, e flui pelo pipeline até alcançar out-file ou set-content, que tenta gravar no mesmo arquivo, mas já está aberto por get-content e você obtém um erro. O parênteses faz com que a operação de leitura de conteúdo seja executada uma vez (aberta, lida e fechada). Só então, quando todas as linhas tiverem sido lidas, elas serão canalizadas uma de cada vez e quando chegarem ao último comando no pipeline, elas poderão ser gravadas no arquivo. É o mesmo que $ content = content; $ content | Onde …

Eu prefiro usar a class File do .NET e seus methods estáticos, como visto no exemplo a seguir.

 $content = [System.IO.File]::ReadAllText("c:\bla.txt").Replace("[MYID]","MyValue") [System.IO.File]::WriteAllText("c:\bla.txt", $content) 

Isso tem a vantagem de trabalhar com uma única string em vez de uma matriz de string como com Get-Content . Os methods também cuidam da codificação do arquivo ( BOM UTF-8, etc.) sem que você tenha que tomar cuidado na maior parte do tempo.

Além disso, os methods não atrapalham os términos de linha (terminações de linha Unix que podem ser usadas) em contraste com um algoritmo que usa Get-Content e canaliza para Set-Content .

Então, para mim: Menos coisas que poderiam quebrar ao longo dos anos.

Uma coisa pouco conhecida ao usar as classs .NET é que, quando você digita “[System.IO.File] ::” na janela do PowerShell, pode pressionar a tecla Tab para percorrer os methods.

O acima só é executado apenas para “One File”, mas você também pode executar isso para vários arquivos dentro de sua pasta:

 Get-ChildItem 'C:yourfile*.xml' -Recurse | ForEach { (Get-Content $_ | ForEach { $_ -replace '[MYID]', 'MyValue' }) | Set-Content $_ } 

Você poderia tentar algo assim:

 $path = "C:\testFile.txt" $word = "searchword" $replacement = "ReplacementText" $text = get-content $path $newText = $text -replace $word,$replacement $newText > $path 

Isso é o que eu uso, mas é lento em arquivos de texto grandes.

 get-content $pathToFile | % { $_ -replace $stringToReplace, $replaceWith } | set-content $pathToFile 

Se você estiver substituindo strings em grandes arquivos de texto e a velocidade for uma preocupação, procure em System.IO.StreamReader e 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() } } 

(O código acima não foi testado.)

Provavelmente, existe uma maneira mais elegante de usar o StreamReader e o StreamWriter para replace texto em um documento, mas isso deve lhe dar um bom ponto de partida.

Isso funcionou para mim usando o diretório de trabalho atual no PowerShell. Você precisa usar a propriedade FullName ou não funcionará na versão 5. do PowerShell. Eu precisei alterar a versão do .NET Framework de destino em TODOS os meus arquivos CSPROJ .

 gci -Recurse -Filter *.csproj | % { (get-content "$($_.FullName)") .Replace('net47', 'net462') | Set-Content "$($_.FullName)"} 

Depois de pesquisar muito, descubro que a linha mais simples para fazer isso sem alterar a codificação é:

 Get-Content path/to/file.ext | out-file -encoding ASCII targetFile.ext 

Um pouco velho e diferente, como eu precisava mudar uma certa linha em todas as instâncias de um nome de arquivo específico.

Além disso, o Set-Content não estava retornando resultados consistentes, então tive que recorrer ao Out-File .

Código abaixo:


 $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 } 

Isso é o que funcionou melhor para mim nesta versão do PowerShell:

Major.Minor.Build.Revision

5.1.16299,98

Pequena correção para o comando Set-Content. Se a string pesquisada não for encontrada, o comando Set-Content deixará em branco (vazio) o arquivo de destino.

Você pode primeiro verificar se a string que você está procurando existe ou não. Se não, não irá replace nada.

 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"}