Powershell 查找和替换循环,OutOfMemoryException
Powershell Find and Replace Loop, OutOfMemoryException
我有一个有效的 powershell 脚本,可以在数千个文件中查找并用新字符串替换几个不同的字符串,而无需更改文件的修改日期。在任何给定的文件中,可能有数百个要替换的字符串实例。文件本身不是很大,可能在 1-50MB 之间(快速浏览一下我正在测试的目录显示最大为 ~33MB)。
我是 运行 Server 2012 R2 虚拟机中的脚本,具有 4 个 vCPU 和 4GB 内存。我已将 Powershell 的 MaxMemoryPerShellMB 值设置为 3GB。如前所述,该脚本有效,但 2-4 小时后 powershell 将开始抛出 OutOfMemoryExceptions 并崩溃。该脚本是 'V2 friendly',我还没有将它采用到 V3+,但我怀疑这是否重要。
我的问题是脚本是否可以改进 prevent/eliminate 我目前 运行 遇到的内存异常。我不介意它运行得慢一点,只要它能完成工作,而不必每隔几个小时检查一次并重新启动它。
$i=0
$all = Get-ChildItem -Recurse -Include *.txt
$scriptfiles = Select-String -Pattern string1,string2,string3 $all
$output = "C:\Temp\scriptoutput.txt"
foreach ($file in $scriptFiles)
{
$filecreate=(Get-ChildItem $file.Path).creationtime
$fileaccess=(Get-ChildItem $file.Path).lastaccesstime
$filewrite=(Get-ChildItem $file.Path).lastwritetime
"$file.Path,Created: $filecreate,Accessed: $fileaccess,Modified: $filewrite" | out-file -FilePath $output -Append
(Get-Content $file.Path) | ForEach-Object {$_ -replace "string1", "newstring" `
-replace "string2", "newstring" `
-replace "string3", "newstring"
} | Set-Content $file.Path
(Get-ChildItem $file.Path).creationtime=$filecreate
(Get-ChildItem $file.Path).lastaccesstime=$fileaccess
(Get-ChildItem $file.Path).lastwritetime=$filewrite
$filecreate=(Get-ChildItem $file.Path).creationtime
$fileaccess=(Get-ChildItem $file.Path).lastaccesstime
$filewrite=(Get-ChildItem $file.Path).lastwritetime
"$file.Path,UPDATED Created: $filecreate,UPDATED Accessed: $fileaccess,UPDATED Modified: $filewrite" | out-file -FilePath $output -Append
$i++}
欢迎大家提出意见、批评和建议。
谢谢
我能看到的最大问题是您为每个 属性 查询重复获取文件。将其替换为每次循环传递一次调用,并将其保存以在传递过程中使用。此外,Out-File 是将数据输出到文件的较慢方法之一。
$output = "C:\Temp\scriptoutput.txt"
$scriptfiles = Get-ChildItem -Recurse -Include *.txt |
Select-String -Pattern string1,string2,string3 |
Select-Object -ExpandProperty Path
$scriptfiles | ForEach-Object{
$file = Get-Item $_
# Save currrent file times
$filecreate=$file.creationtime
$fileaccess=$file.lastaccesstime
$filewrite=$file.lastwritetime
"$file,Created: $filecreate,Accessed: $fileaccess,Modified: $filewrite"
# Update content.
(Get-Content $file) -replace "string1", "newstring" `
-replace "string2", "newstring" `
-replace "string3", "newstring" | Set-Content $file
# Write all the original times back.
$file.creationtime=$filecreate
$file.lastaccesstime=$fileaccess
$file.lastwritetime=$filewrite
# Verify the changes... Should not be required but it is what you were doing.
$filecreate=$file.creationtime
$fileaccess=$file.lastaccesstime
$filewrite=$file.lastwritetime
"$file,UPDATED Created: $filecreate,UPDATED Accessed: $fileaccess,UPDATED Modified: $filewrite"
} | Set-Content $output
未测试但应该没问题。
根据您替换的实际情况,您也可以在那里节省一些时间。显然在生产 运行 之前先测试。
我删除了你的计数器,因为它在代码中没有出现。
您的日志记录可以很容易地基于 csv,因为您已准备好所有对象,但我只想确保我们在走得更远之前是正确的轨道。
我有一个有效的 powershell 脚本,可以在数千个文件中查找并用新字符串替换几个不同的字符串,而无需更改文件的修改日期。在任何给定的文件中,可能有数百个要替换的字符串实例。文件本身不是很大,可能在 1-50MB 之间(快速浏览一下我正在测试的目录显示最大为 ~33MB)。
我是 运行 Server 2012 R2 虚拟机中的脚本,具有 4 个 vCPU 和 4GB 内存。我已将 Powershell 的 MaxMemoryPerShellMB 值设置为 3GB。如前所述,该脚本有效,但 2-4 小时后 powershell 将开始抛出 OutOfMemoryExceptions 并崩溃。该脚本是 'V2 friendly',我还没有将它采用到 V3+,但我怀疑这是否重要。
我的问题是脚本是否可以改进 prevent/eliminate 我目前 运行 遇到的内存异常。我不介意它运行得慢一点,只要它能完成工作,而不必每隔几个小时检查一次并重新启动它。
$i=0
$all = Get-ChildItem -Recurse -Include *.txt
$scriptfiles = Select-String -Pattern string1,string2,string3 $all
$output = "C:\Temp\scriptoutput.txt"
foreach ($file in $scriptFiles)
{
$filecreate=(Get-ChildItem $file.Path).creationtime
$fileaccess=(Get-ChildItem $file.Path).lastaccesstime
$filewrite=(Get-ChildItem $file.Path).lastwritetime
"$file.Path,Created: $filecreate,Accessed: $fileaccess,Modified: $filewrite" | out-file -FilePath $output -Append
(Get-Content $file.Path) | ForEach-Object {$_ -replace "string1", "newstring" `
-replace "string2", "newstring" `
-replace "string3", "newstring"
} | Set-Content $file.Path
(Get-ChildItem $file.Path).creationtime=$filecreate
(Get-ChildItem $file.Path).lastaccesstime=$fileaccess
(Get-ChildItem $file.Path).lastwritetime=$filewrite
$filecreate=(Get-ChildItem $file.Path).creationtime
$fileaccess=(Get-ChildItem $file.Path).lastaccesstime
$filewrite=(Get-ChildItem $file.Path).lastwritetime
"$file.Path,UPDATED Created: $filecreate,UPDATED Accessed: $fileaccess,UPDATED Modified: $filewrite" | out-file -FilePath $output -Append
$i++}
欢迎大家提出意见、批评和建议。
谢谢
我能看到的最大问题是您为每个 属性 查询重复获取文件。将其替换为每次循环传递一次调用,并将其保存以在传递过程中使用。此外,Out-File 是将数据输出到文件的较慢方法之一。
$output = "C:\Temp\scriptoutput.txt"
$scriptfiles = Get-ChildItem -Recurse -Include *.txt |
Select-String -Pattern string1,string2,string3 |
Select-Object -ExpandProperty Path
$scriptfiles | ForEach-Object{
$file = Get-Item $_
# Save currrent file times
$filecreate=$file.creationtime
$fileaccess=$file.lastaccesstime
$filewrite=$file.lastwritetime
"$file,Created: $filecreate,Accessed: $fileaccess,Modified: $filewrite"
# Update content.
(Get-Content $file) -replace "string1", "newstring" `
-replace "string2", "newstring" `
-replace "string3", "newstring" | Set-Content $file
# Write all the original times back.
$file.creationtime=$filecreate
$file.lastaccesstime=$fileaccess
$file.lastwritetime=$filewrite
# Verify the changes... Should not be required but it is what you were doing.
$filecreate=$file.creationtime
$fileaccess=$file.lastaccesstime
$filewrite=$file.lastwritetime
"$file,UPDATED Created: $filecreate,UPDATED Accessed: $fileaccess,UPDATED Modified: $filewrite"
} | Set-Content $output
未测试但应该没问题。
根据您替换的实际情况,您也可以在那里节省一些时间。显然在生产 运行 之前先测试。
我删除了你的计数器,因为它在代码中没有出现。
您的日志记录可以很容易地基于 csv,因为您已准备好所有对象,但我只想确保我们在走得更远之前是正确的轨道。