Powershell查找和替换循环,OutOfMemoryException

Powershell查找和替换循环,OutOfMemoryException,powershell,recursion,replace,out-of-memory,Powershell,Recursion,Replace,Out Of Memory,我有一个正在运行的powershell脚本,可以在数千个文件中查找几个不同的字符串,并将其替换为新字符串,而无需更改文件的修改日期。在任何给定的文件中,都可能有数百个上述字符串的实例需要替换。文件本身不是很大,可能在1-50MB之间(快速浏览一下我正在测试的目录就会发现最大的约33MB) 我正在一个服务器2012 R2虚拟机中运行脚本,该虚拟机有4个vCPU和4GB内存。我已将Powershell的MaxMemoryPerShellMB值设置为3GB。如前所述,该脚本可以工作,但在2-4小时后,

我有一个正在运行的powershell脚本,可以在数千个文件中查找几个不同的字符串,并将其替换为新字符串,而无需更改文件的修改日期。在任何给定的文件中,都可能有数百个上述字符串的实例需要替换。文件本身不是很大,可能在1-50MB之间(快速浏览一下我正在测试的目录就会发现最大的约33MB)

我正在一个服务器2012 R2虚拟机中运行脚本,该虚拟机有4个vCPU和4GB内存。我已将Powershell的MaxMemoryPerShellMB值设置为3GB。如前所述,该脚本可以工作,但在2-4小时后,powershell将开始抛出OutOfMemoryExceptions并崩溃。脚本是“V2友好”的,我还没有将其应用到V3+中,但我怀疑这是否太重要了

我的问题是是否可以改进脚本以防止/消除我目前遇到的内存异常。我不介意它运行得慢一点,只要它可以在不需要每隔几个小时检查一次并重新启动的情况下完成任务

$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++}
欢迎任何评论、批评和建议


谢谢

我能看到的最大问题是,您正在重复获取您查询的每个属性的文件。将其替换为每个循环传递一个调用,并将其保存以在传递过程中使用。输出文件也是将数据输出到文件的较慢方法之一

$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,因为你已经准备好了所有的对象,但我只是想确保我们是一个正确的轨道之前,我们去远

我所能看到的最大问题是,您正在重复获取查询的每个属性的文件。将其替换为每个循环传递一个调用,并将其保存以在传递过程中使用。输出文件也是将数据输出到文件的较慢方法之一

$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,因为你已经准备好了所有的对象,但我只是想确保我们是一个正确的轨道之前,我们去远

您显示的字符串替换示例。。。。它们是单独的替换,还是您找到3个字符串并用相同的东西替换它们?您展示的字符串替换示例。。。。它们是单独的替代品,还是找到3个字符串并用相同的东西替换它们?存储创建时间,除非你想记录它,因为它不会改变,这有点毫无意义。谢谢Matt。我现在正在运行脚本,它似乎工作得更好。时间会告诉我们它是否解决了OutOfMemoryException(我相信它会)。最终,所有时间戳都被记录下来,作为脚本执行所需功能的证据。一旦脚本经过适当的测试和审查,它将在5年以上的数据中进行扫描,记录的时间戳只是证明它正在做它应该做的事情。@jw1n5您怎么知道它运行得更好?我正在监视资源(工作集)利用率并打开测试目录中文件的句柄。与上一个脚本相比,系统的总体利用率较低,并且处理文件的速度似乎也比上一个版本稍快。请注意,我的#验证更改代码不准确,因为对象未更新。老实说,我还是会删除这个部分,因为它看起来是多余的。存储创建时间,除非你想记录它,是没有意义的,因为它不会改变。谢谢Matt。我现在正在运行脚本,它似乎工作得更好。时间会告诉我们它是否解决了OutOfMemoryException(我相信它会)。最终,所有时间戳都被记录下来,作为脚本执行所需功能的证据。一旦脚本经过适当的测试和审查,它将在5年以上的数据中进行扫描,记录的时间戳只是证明它正在做它应该做的事情。@jw1n5您怎么知道它运行得更好?我正在监视资源(工作集)利用率并打开测试目录中文件的句柄。与上一个脚本相比,系统的总体利用率较低,并且处理文件的速度似乎也比上一个版本稍快。请注意,我的#验证更改代码不准确,因为对象未更新。老实说,我会删除这部分反正它似乎是多余的。