如何用PowerShell替换文件中每次出现的String?

问题描述 投票:248回答:11

使用PowerShell,我想用[MYID]替换给定文件中所有确切出现的MyValue。最简单的方法是什么?

powershell
11个回答
377
投票

使用(V3版):

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

或者对于V2:

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

-1
投票

Set-Content命令的小校正。如果未找到搜索到的字符串,则Set-Content命令将空白(清空)目标文件。

您可以先验证您要查找的字符串是否存在。如果不是,它将不会取代任何东西。

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

-2
投票

在搜索了太多之后,我想出了最简单的一行来做到这一点而不改变编码是:

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

86
投票
(Get-Content file.txt) | 
Foreach-Object {$_ -replace '\[MYID\]','MyValue'}  | 
Out-File file.txt

请注意(Get-Content file.txt)周围的括号是必需的:

如果没有括号,则一次读取一行内容,然后沿着管道向下流动,直到它到达out-file或set-content,这会尝试写入同一个文件,但它已经被get-content打开了,你得到了一个错误。括号使内容读取的操作执行一次(打开,读取和关闭)。只有当所有行都被读取时,它们才会一次一个地传送,当它们到达管道中的最后一个命令时,它们就可以写入文件。它与$ content = content相同; $ content |哪里......


71
投票

我更喜欢使用.NET的File-class及其静态方法,如以下示例所示。

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

Get-Content一样,这样做的好处是可以使用单个String而不是String-array。这些方法还可以处理文件的编码(UTF-8 BOM等),而无需在大多数情况下进行操作。

与使用Get-Content和管道到Set-Content的算法相比,这些方法不会弄乱行结尾(可能使用的Unix行结尾)。

所以对我来说:这些年来可能会破坏的事情会减少。

使用.NET类时,一个鲜为人知的事情是,当您在PowerShell窗口中键入“[System.IO.File] ::”时,可以按Tab键逐步执行这些方法。


19
投票

上面的那个只运行“One File”,但你也可以为你文件夹中的多个文件运行:

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

10
投票

你可以尝试这样的事情:

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

7
投票

这是我使用的,但它在大文本文件上很慢。

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

如果您要替换大文本文件中的字符串并且速度是一个问题,请查看使用System.IO.StreamReaderSystem.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()
   }
}

(上面的代码尚未经过测试。)

使用StreamReader和StreamWriter替换文档中的文本可能有一种更优雅的方式,但这应该会给你一个很好的起点。


0
投票

这对我使用PowerShell中的当前工作目录。您需要使用FullName属性,否则它将无法在PowerShell版本5中运行。我需要在所有CSPROJ文件中更改目标.NET框架版本。

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

0
投票

有点旧和不同,因为我需要在特定文件名的所有实例中更改某一行。

此外,Set-Content没有返回一致的结果,所以我不得不求助于Out-File

代码如下:


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

这是PowerShell版本最适合我的方法:

Major.Minor.Build.Revision

5.1.16299.98


0
投票

归功于@ rominator007

我把它包装成一个函数(因为你可能想再次使用它)

function Replace-AllStringsInFile($SearchString,$ReplaceString,$FullPathToFile)
{
    $content = [System.IO.File]::ReadAllText("$FullPathToFile").Replace("$SearchString","$ReplaceString")
    [System.IO.File]::WriteAllText("$FullPathToFile", $content)
}

注意:这不是区分大小写!!!!!

看到这篇文章:String.Replace ignoring case

© www.soinside.com 2019 - 2024. All rights reserved.