Answers:
使用(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
(Get-Content file.txt) |
Foreach-Object {$_ -replace '\[MYID\]','MyValue'} |
Out-File file.txt
请注意,圆括号(Get-Content file.txt)
必须为:
如果没有括号,则一次读取一行内容,然后沿管道向下流动,直到到达文件外或set-content为止,后者尝试写入同一文件,但是已经由get-content打开,您可以一个错误。括号使内容读取操作执行一次(打开,读取和关闭)。只有这样,当所有行都被读取后,它们才会一次被管道传输,并且当它们到达管道中的最后一条命令时,它们才能被写入文件。与$ content = content相同;$ content | 哪里...
Set-Content
而不是Out-File
yuou会收到类似“该进程无法访问文件'123.csv”的警告,因为该文件正在被另一个进程使用。” 。
Get-Content
括号括起来可以起作用。您能否在答案中解释为什么需要括号?我仍将替换Out-File
为Set-Content
它,因为它更安全。如果您忘记了括号,它可以防止您清除目标文件。
Set-Content
代替Out-File
是更好,更安全的解决方案。对不起,必须投票。
我更喜欢使用.NET的File-class及其静态方法,如以下示例所示。
$content = [System.IO.File]::ReadAllText("c:\bla.txt").Replace("[MYID]","MyValue")
[System.IO.File]::WriteAllText("c:\bla.txt", $content)
这具有使用单个String代替Get-Content的String-array的优势。这些方法还可以处理文件的编码(UTF-8 BOM等),而无需您花费很多时间。
与使用Get-Content并通过管道传递到Set-Content的算法相比,这些方法也不会弄乱行尾(可能会使用Unix行尾)。
所以对我来说:多年来可能会发生的事情减少了。
使用.NET类时,鲜为人知的是,在PowerShell窗口中键入“ [System.IO.File] ::”后,您可以Tab按键以逐步浏览那里的方法。
[System.IO.File] | gm
C:\Windows\System32\WindowsPowerShell\v1.0
?
上面的一个仅适用于“一个文件”,但是您也可以针对文件夹中的多个文件运行该文件:
Get-ChildItem 'C:yourfile*.xml' -Recurse | ForEach {
(Get-Content $_ | ForEach { $_ -replace '[MYID]', 'MyValue' }) |
Set-Content $_
}
foreach
您还可以执行此操作Get-ChildItem 'C:\folder\file*.xml' -Recurse | ForEach { (Get-Content $_).Replace('[MYID]', 'MyValue') | Set-Content $_ }
foreach
,因为Get-Content会执行您可能不希望的操作...它返回一个字符串数组,其中每个字符串都是文件中的一行。如果要循环浏览的目录(和子目录)与运行脚本的位置不同,则需要这样的目录: Get-ChildItem $Directory -File -Recurse | ForEach { (Get-Content $_.FullName) | ForEach { $_ -replace '[MYID]', 'MyValue' } | Set-Content $_.FullName }
其中$Directory
包含要修改的文件的目录在哪里。
这就是我使用的方法,但是在大型文本文件上速度很慢。
get-content $pathToFile | % { $_ -replace $stringToReplace, $replaceWith } | set-content $pathToFile
如果要替换大型文本文件中的字符串,并且需要考虑速度,请考虑使用System.IO.StreamReader和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()
}
}
(以上代码尚未经过测试。)
使用StreamReader和StreamWriter替换文档中的文本可能是一种更为优雅的方法,但这应该为您提供一个良好的起点。
我从Payette的Windows Powershell in Action中找到了一种鲜为人知但非常酷的方法。您可以引用类似于$ env:path的变量之类的文件,但需要添加花括号。
${c:file.txt} = ${c:file.txt} -replace 'oldvalue','newvalue'
$myFile
怎么办?
$a = 'file.txt'; invoke-expression "`${c:$a} = `${c:$a} -replace 'oldvalue','newvalue'"
如果您需要替换多个文件中的字符串:
应该注意的是,此处完成的时间可能与此处发布的不同方法完全不同。对我来说,我经常有大量的小文件。为了测试最高性能,我在40,693个单独的文件中提取了5.52 GB(5,933,604,999字节)的XML,并通过以下三个答案进行了测试:
## 5.52 GB (5,933,604,999 bytes) of XML files (40,693 files)
#### Test 1 - Plain Replace
$start = get-date
$xmls = (Get-ChildItem -Path "I:\TestseT\All_XML" -Recurse -Filter *.xml).FullName
foreach ($xml in $xmls)
{
(Get-Content $xml).replace("'", " ") | Set-Content $xml
}
$end = get-date
NEW-TIMESPAN –Start $Start –End $End
<#
TotalMinutes: 103.725113128333
#>
#### Test 2 - Replace with -Raw
$start = get-date
$xmls = (Get-ChildItem -Path "I:\TestseT\All_XML" -Recurse -Filter *.xml).FullName
foreach ($xml in $xmls)
{
(Get-Content $xml -Raw).replace("'", " ") | Set-Content $xml
}
$end = get-date
NEW-TIMESPAN –Start $Start –End $End
<#
TotalMinutes: 10.1600227983333
#>
#### Test 3 - .NET, System.IO
$start = get-date
$xmls = (Get-ChildItem -Path "I:\TestseT\All_XML" -Recurse -Filter *.xml).FullName
foreach ($xml in $xmls)
{
$txt = [System.IO.File]::ReadAllText("$xml").Replace("'"," ")
[System.IO.File]::WriteAllText("$xml", $txt)
}
$end = get-date
NEW-TIMESPAN –Start $Start –End $End
<#
TotalMinutes: 5.83619516833333
#>
归功于@ rominator007
我将其包装到一个函数中(因为您可能想再次使用它)
function Replace-AllStringsInFile($SearchString,$ReplaceString,$FullPathToFile)
{
$content = [System.IO.File]::ReadAllText("$FullPathToFile").Replace("$SearchString","$ReplaceString")
[System.IO.File]::WriteAllText("$FullPathToFile", $content)
}
注意:这不区分大小写!
参见这篇文章:String.Replace忽略大小写
使用PowerShell中的当前工作目录,这对我有用。您需要使用该FullName
属性,否则在PowerShell版本5中将无法使用。我需要在所有CSPROJ
文件中更改目标.NET Framework版本。
gci -Recurse -Filter *.csproj |
% { (get-content "$($_.FullName)")
.Replace('<TargetFramework>net47</TargetFramework>', '<TargetFramework>net462</TargetFramework>') |
Set-Content "$($_.FullName)"}
有点陈旧和不同,因为我需要在特定文件名的所有实例中更改特定的行。
另外,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版本上,这是最适合我的方法:
主要,次要,构建,修订
5.1.16299.98
对“设置内容”命令进行了较小的更正。如果找不到搜索到的字符串,该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"}
set-content test.txt "hello hello world hello world hello"
,然后(get-content .\test.txt).Replace("something", "awesome") | set-content .\test.txt
不会按照此建议清空文件。