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

ama*_*eur 270 powershell

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

Loï*_*HEL 414

使用(V3版):

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

或者对于V2:

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

  • 警告:针对大文件(几百兆字节左右)运行这些脚本会占用大量内存.如果您在生产服务器上运行,请确保您有足够的空间:D (5认同)
  • 如果要保存修改,@ rob将结果传递给set-content或out-file (4认同)
  • 谢谢 - 我收到错误"替换:方法调用失败,因为[System.Object []]不包含名为'replace'的方法." 虽然? (3认同)
  • 我收到错误_**"方法调用失败,因为[System.Object []]不包含名为'replace'的方法."**_因为我试图在只有V2的机器上运行V3版本. (2认同)

Sha*_*evy 89

(Get-Content file.txt) | 
Foreach-Object {$_ -replace '\[MYID\]','MyValue'}  | 
Out-File file.txt
Run Code Online (Sandbox Code Playgroud)

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

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

  • 当get-content在括号中时,不应该发生这种情况.它们会导致操作打开,读取和关闭文件,因此不会发生错误.你能用示例文本文件再次测试它吗? (9认同)
  • 问题与***文件编码UTF-8***.保存文件时,更改编码.不一样.http://stackoverflow.com/questions/5596982/using-powershell-to-write-a-file-in-utf-8-without-the-bom.我认为**set-content**考虑编码文件(如UTF-8).但不是**Out-File** (6认同)
  • 如果可以的话,我会将我的upvote改为downvote.在PowerShell 3中,这会以静默方式删除文件中的所有内容!使用`Set-Content`而不是`Out-File`你得到一个类似*的警告"该进程无法访问文件'123.csv',因为它正被另一个进程使用."*. (5认同)
  • 在括号中使用`Get-Content`它可以工作.你能在答案中解释为什么括号是必要的吗?我仍然会用`Set-Content`替换`Out-File`,因为它更安全; 如果忘记括号,它可以防止您擦除目标文件. (2认同)
  • 该解决方案不必要地具有误导性,并在我使用它时引起了问题。我正在更新一个由安装过程立即使用的配置文件。配置文件仍由进程持有,安装失败。使用 `Set-Content` 而不是 `Out-File` 是更好、更安全的解决方案。抱歉不得不投反对票。 (2认同)

rom*_*007 75

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

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

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

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

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

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

  • 这比为不同版本的Powershell编写不同的代码要容易得多. (2认同)
  • 这种方法似乎也是最快的。结合上述的好处,问题应该是:“为什么你想使用其他东西?” (2认同)

小智 21

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

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

  • “上面那个”的答案是什么? (3认同)
  • 实际上,您确实需要内部“foreach”,因为 Get-Content 做了一些您可能意想不到的事情...它返回一个字符串数组,其中每个字符串都是文件中的一行。如果您循环访问的目录(和子目录)与正在运行的脚本位于不同的位置,您将需要如下所示的内容:`Get-ChildItem $Directory -File -Recurse | Get-ChildItem $Directory -File -Recurse | ForEach { (获取内容 $_.FullName) | ForEach { $_ -replace '[MYID]', 'MyValue' } | Set-Content $_.FullName }` 其中 `$Directory` 是包含要修改的文件的目录。 (2认同)

Ric*_*ard 10

你可以尝试这样的事情:

$path = "C:\testFile.txt"
$word = "searchword"
$replacement = "ReplacementText"
$text = get-content $path 
$newText = $text -replace $word,$replacement
$newText > $path
Run Code Online (Sandbox Code Playgroud)


Dar*_*vey 7

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

get-content $pathToFile | % { $_ -replace $stringToReplace, $replaceWith } | set-content $pathToFile
Run Code Online (Sandbox Code Playgroud)

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

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

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


DBA*_*Don 6

如果您需要替换多个文件中的字符串:

\n

应该注意的是,此处发布的不同方法在完成时间方面可能会有很大差异。对于我来说,我经常有大量的小文件。为了测试什么性能最好,我在 40,693 个单独的文件中提取了 5.52 GB(5,933,604,999 字节)的 XML,并运行了我在这里找到的三个答案:

\n
## 5.52 GB (5,933,604,999 bytes) of XML files (40,693 files) \n$xmls = (Get-ChildItem -Path "I:\\TestseT\\All_XML" -Recurse -Filter *.xml).FullName\n\n#### Test 1 - Plain Replace\n$start = Get-Date\nforeach ($xml in $xmls) {\n    (Get-Content $xml).replace("'", " ") | Set-Content $xml\n}\n$end = Get-Date\nNew-TimeSpan \xe2\x80\x93Start $Start \xe2\x80\x93End $End\n# TotalMinutes: 103.725113128333\n\n#### Test 2 - Replace with -Raw\n$start = Get-Date\nforeach ($xml in $xmls) {\n    (Get-Content $xml -Raw).replace("'", " ") | Set-Content $xml\n}\n$end = Get-Date\nNew-TimeSpan \xe2\x80\x93Start $Start \xe2\x80\x93End $End\n# TotalMinutes: 10.1600227983333\n\n#### Test 3 - .NET, System.IO\n$start = Get-Date\nforeach ($xml in $xmls) {\n    $txt = [System.IO.File]::ReadAllText("$xml").Replace("'"," ") \n    [System.IO.File]::WriteAllText("$xml", $txt)\n}\n$end = Get-Date\nNew-TimeSpan \xe2\x80\x93Start $Start \xe2\x80\x93End $End\n# TotalMinutes: 5.83619516833333\n
Run Code Online (Sandbox Code Playgroud)\n

  • 这可能就是为什么答案在顶部指出“如果您需要替换多个文件中的文本”。 (5认同)

Kol*_*yon 5

感谢@rominator007

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

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

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

请参阅这篇文章:String.Replace 忽略大小写


js2*_*010 5

我从 Payette 的Windows Powershell in Action 中找到了一种鲜为人知但非常酷的方法。可以引用变量之类的文件,类似于$env:path,但是需要加花括号。

${c:file.txt} = ${c:file.txt} -replace 'oldvalue','newvalue'
Run Code Online (Sandbox Code Playgroud)

  • 好的。但距离 Linux 的 `sed -i "s/oldValue/newValue/g" file.txt` 还很远:-( (4认同)