【问题标题】:PowerShell foreach file in folder using a lot of memoryPowerShell foreach 文件夹中的文件使用大量内存
【发布时间】:2011-08-23 02:31:36
【问题描述】:

我对 PowerShell 和编程还很陌生,所以我确信这段代码可以进一步优化。

然而,我对这段代码最大的问题是它将来自 foreach 循环的数据放入内存中。防止这种情况的最佳方法是什么?

我开始使用在此处找到的脚本重写此脚本:PowerShell: delete files older than x days

编辑:哦,顺便说一下,每个 TargetFolder 中大约有 60 000~ 个文件。

## Set Window Title
$title = $Host.UI.RawUI.WindowTitle
$titdef = $title
$Host.UI.RawUI.WindowTitle = "Checking files. Do NOT close " + "($title)"

## Debug (0=Nothing, 1=Extended log, 2=Extended log+Screen)
$Debug = "2"

## Log file
$log = "C:\status\status.txt"
Add-Content $log "nul"

## Clear log before adding new data 
Remove-Item $log

## Folders to check
$TargetFolder1 = “\\server\c$\BiztalkProjects\UDC\output\ume”
$TargetFolder2 = “\\server\c$\BiztalkProjects\UDC\output\upp”

## Required stuff
$Now = Get-Date
$Days = “2”
$TwoDays = $Now.AddDays(-$Days)
$Folder1 = get-childitem $TargetFolder1 -include *
$Folder2 = get-childitem $TargetFolder2 -include *

## Window Message
"Checking for files newer than $Days days in:"
"$TargetFolder1"
"$TargetFolder2"
""
"This may take 5-15 minutes . . . (Will commit about 250MB~ RAM during the period)"

## Reset variable $OK to "0"
$OK = 0

if ($Debug -eq "2") {"Check1"}
foreach ($File in $Folder1) {
    $FileLastWrite = $File.LastWriteTime
    if ($File.LastWriteTime -le $TwoDays) {
        $OK = $OK + "0"
        if ($Debug -eq "2") {
            "OK + 0 (=$OK) $File ($FileLastWrite)"
            }
    } else {
        $OK = $OK + "1"
        if ($Debug -eq "2") {
            "OK + 1 (=$OK) $File ($FileLastWrite)" 
            }
    }
}
if ($Debug -eq "2") {"Check2"}
foreach ($File in $Folder2) {
    $FileLastWrite = $File.LastWriteTime
    if ($File.LastWriteTime -le $TwoDays) {
        $OK = $OK + "0"
        if ($Debug -eq "2") {
            "OK + 0 (=$OK) $File ($FileLastWrite)"
            }
    } else {
        $OK = $OK + "1"
        if ($Debug -eq "2") {
            "OK + 1 (=$OK) $File ($FileLastWrite)" 
            }
    }
}
if ($OK -gt "0") {
    if ($Debug -eq "2") {
        ""
        "Found $OK file(s) newer than $Days days"
        }
    Add-Content $log "OK"
    if ($Debug -ge "1") {
        Add-Content $log "Found $OK file(s) newer than $Days days"
        }
    }
    else {
        if ($Debug -eq "2") {
        ""
        "Found $OK file(s) newer than $Days days"
        }
    Add-Content $log "Error"
    if ($Debug -ge "1") {
        Add-Content $log "Found $OK file(s) newer than $Days days"
        }
    }
$Host.UI.RawUI.WindowTitle = $titdef

此版本有效。感谢所有帮助过的人!

## Set Window Title
$title = $Host.UI.RawUI.WindowTitle
$titdef = $title
$Host.UI.RawUI.WindowTitle = "Checking files. Do NOT close " + "($title)"

## Debug (0=Nothing, 1=Extended log, 2=Extended log+Screen)
$Debug = "2"

## Log file
$log = "C:\status\status.txt"
Add-Content $log "nul"

## Clear log before adding new data 
Remove-Item $log

## Folders to check
$TargetFolder1 = “\\server\c$\BiztalkProjects\UDC\output\ume”
$TargetFolder2 = “\\server\c$\BiztalkProjects\UDC\output\upp”

## Required stuff
$Now = Get-Date
$Days = “2”
$TwoDays = $Now.AddDays(-$Days)

## Window Message
"Checking for files newer than $Days days in:"
"$TargetFolder1"
"$TargetFolder2"
""
"This may take 5-15 minutes . . ."

## Reset variable $OK to "0"
$OK = 0

get-childitem $TargetFolder1,$TargetFolder2 -filter *.xml |where-object {
    $FileLastWrite = $_.LastWriteTime
    if ($FileLastWrite -le $TwoDays) {
        $OK = $OK + "0"
    } else {
        $OK = $OK + "1"
    }
}
if ($OK -gt "0") {
    if ($Debug -eq "2") {
        ""
        "Found $OK file(s) newer than $Days days"
        }
    Add-Content $log "OK"
    if ($Debug -ge "1") {
        Add-Content $log "Found $OK file(s) newer than $Days days"
        }
    }
    else {
        if ($Debug -eq "2") {
        ""
        "Found $OK file(s) newer than $Days days"
        }
    Add-Content $log "Error"
    if ($Debug -ge "1") {
        Add-Content $log "Found $OK file(s) newer than $Days days"
        }
    }
$Host.UI.RawUI.WindowTitle = $titdef

【问题讨论】:

    标签: memory powershell foreach


    【解决方案1】:

    试试:

    $Folder1 = 获取子项 $TargetFolder1 -filter *.xml

    比使用include(在大量文件上)性能要好得多。甚至更好:

    get-childitem $TargetFolder1 -filter *.xml | % {}

    编辑

    我明白了,你的问题是由于include!如果您需要使用通配符和正则表达式来获取子项,请改用filter。由于不使用正则表达式匹配,因此性能更高。

    试试:

    $Folder1 = 获取子项 $TargetFolder1 -filter *

    即使不是很清楚你在那里过滤了什么(或者你想要包含)......一切?可能你根本不需要通配符。


    以我的拙见,防止内存中的foreach循环的最佳方法是管道。但是,由于脚本的性质(循环内有许多嵌套控件),这并不容易。

    尝试:

    $文件夹1 | % { # ... }

    即使我怀疑它会提高性能。


    或:

    获取子项 $TargetFolder1 -include * | % { # ... }

    【讨论】:

    • 一个好的重构(使用函数,更好的日志系统......)应该会更容易
    • 你的意思是我应该用你后来的建议替换我的 foreach 循环吗?不会也循环遍历所有文件,首先当它结束时会清理内存?
    • 我正在尝试查找是否有超过 2 天的新文件。如果不是,则将文件放在那里的应用程序已经崩溃。它们是 XML 文件。所以这可能是这样做的方法吗? get-childitem $TargetFolder2 -filter *.xml
    • 我用 $FolderX | 替换了 foreach 循环。 % {} 和 -include 和 -filter。它仍然使用 200MB+ 并且仍然需要很长时间才能完成。 (10 分钟~)
    • 试试get-childitem $TargetFolder1 -filter *.xml | % { 否则你真的需要按照@Cedric 的建议重构你的代码。
    【解决方案2】:

    类似

     $TS=(Get-Date).AddDays(-2)
     $COUNT = (get-childitem -recurse path1,path2 |where-object {$_.LastWriteTime -gt $TS}|measure-object).Count
     if ($COUNT > 0) { Echo "There are $COUNT files" }
    

    可能会更好(我还没有测试过它的性能)

    【讨论】:

    • 谢谢,这似乎更好地优化了。但我不是要删除文件:) 我只是在有超过 2 天的新文件时才尝试获取信息,如果没有,我会通过电子邮件或其他方式通知我。
    • 对不起,我刚刚看到你提到删除文件,但没有正确检查那是你想要的!无论如何,你现在对 empo 的回答很满意。
    • 现在更新了代码,只计算匹配的文件并测试答案。
    • 你的版本很精简——我喜欢。虽然很难接受两个答案:-\
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-12-31
    • 2017-12-22
    • 2018-07-08
    • 1970-01-01
    • 2018-07-31
    • 2018-04-02
    • 2012-04-03
    相关资源
    最近更新 更多