2017-06-05 45 views
2

我为一个实习编写了一个简单的脚本,它通过提供的目录浏览并删除任何超过指定天数的文件。今天我花了我所有的空闲时间试图收紧它。下面是我到目前为止有:我可以让这个脚本更快吗?

function delOld($dir, $numDays){ 
    $timespan = new-timespan -days $numDays 
    $curTime = get-date 
    get-childItem $dir -Recurse -file | 
    where-object {(($curTime)-($_.LastWriteTime)) -gt $timespan} | 
    remove-Item -whatif 
} 

下面是函数的调用的例子:

delOld -dir "C:\Users\me\Desktop\psproject" -numDays 5 

对不起,我阅读的难度,我发现,冷凝操作成一行比每次迭代将其重新分配给易读变量更有效。目前为了测试目的,删除项目已被删除。我知道,在这一点上,我可能无法加快它的速度,但是,我将它运行在一个TB文件上,因此每个操作都很重要。

在此先感谢您提供的任何建议!

+0

这是一样快,你可以把它给我的眼睛。我真的不知道,除了设计它可能导致失业之外,它会如何更快?但重新设计这样会否定速度增加 – pointerless

+0

您是否尝试过Log Parser? –

+1

99%的时间花在读取物理磁盘的'Get-ChildItem'上,所以如果有任何方法加速它的存在,它将直接通过使用[Everything's API]来读取磁盘的MFT(http://www.voidtools.com/support/everything/sdk /)(应该启用时间/日期索引)并且可能需要几秒钟的搜索查询! – wOxxOm

回答

1

许多PowerShell cmdlet比它们的.NET相当慢。例如,您可以拨打[System.IO.File]::Delete($_.FullName),然后查看是否存在性能差异。 Get-ChildItem =>[System.IO.Directory]::GetFiles(...)也是如此。

为此,我会编写一个小脚本,用于创建两个临时文件夹,每个文件夹中有100,000个空的测试文件。然后调用[System.Diagnostics.StopWatch]中封装的函数的每个版本。

一些示例代码:

$stopwatch = New-Object 'System.Diagnostics.StopWatch' 
$stopwatch.Start() 

Remove-OldItems1 ... 

$stopwatch.Stop() 
Write-Host $stopwatch.ElapsedMilliseconds 

$stopwatch.Reset() 
$stopwatch.Start() 

Remove-OldItems2 ... 

$stopwatch.Stop() 
Write-Host $stopwatch.ElapsedMilliseconds 

为PowerShell的更多的印象分:运行Get-Verb在PowerShell窗口中,你可以看到批准的动词列表。建议PowerShell中的函数名称为Verb-Noun,因此类似于Remove-OldItems可能适合该账单。

+2

等效.net方法是否更快完全取决于使用情况。许多PowerShell cmdlet被编写为接受管道输入并在多个项目上运行,但是人们将其转到“ForEach-Object”,然后在每个单独项目的块内调用该cmdlet。这种方法的问题在于,cmdlet中的set/teardown代码会针对每个项目运行,而如果项目是通过管道运行的,则只会运行一次。这只是一个如何减慢速度和cmdlet的例子,但这一切都取决于上下文,因此测试很好。 – briantist

+0

这个答案没有提到非SSD磁盘速度(随机查找+读取)*比PS cmdlet与.NET方法之间的差异慢了多个数量级*。 – wOxxOm

+0

@ briantist:同意。 OP应该写快速性能测试。直到你尝试才知道,除非你确实知道两个函数的内部结构。 –

5

在PowerShell和.NET方法境界住,这里是你如何能加快你的函数:

  • 计算截止时间戳记一次,在前面。

  • 使用[IO.DirectoryInfo]类型的EnumerateFiles()方法(PSv3 +/.NET4 +)与foreach声明组合。 顶端的帽子到wOxxOm

    • EnumerateFiles()列举文件一次一个,保持内存使用常量,类似,但速度比Get-ChildItem

      • 注意事项

        • EnumerateFiles()总是包括隐藏文件,而Get-ChildItem默认情况下不包括他们,只有他们包括如果指定-Force
        • 如果有遇到无法访问的目录由于缺乏权限的机会,封闭整个foreach声明在try/catch块,以确保所有文件访问进行处理。

        • 枚举顺序可以不同于Get-ChildItem

    • PowerShell的foreach声明ForEach-Objectcmdlet的快得多,而且也比PSv4 + .ForEach()收集运营商更快。

  • 直接调用上循环体内部的各[System.IO.FileInfo]实例.Delete()方法。

注意:为简便起见,有在以下的函数中没有错误的检查,如为$numDays是否具有容许值和是否$dir是指现有的目录(如果它是基于一个路径定制PS驱动器,你必须首先用Convert-Path解决它)。

function delOld($dir, $numDays) { 
    $dtCutoff = [datetime]::now - [timespan]::FromDays($numDays) 
    # Make sure that the .NET framework's current dir. is the same as PS's: 
    [System.IO.Directory]::SetCurrentDirectory($PWD.ProviderPath) 
    # Enumerate all files recursively. 
    # Replace $file.FullName with $file.Delete() to perform actual deletion. 
    foreach ($file in ([IO.DirectoryInfo] $dir).EnumerateFiles('*', 'AllDirectories')) { 
    if ($file.LastWriteTime -lt $dtCutOff) { $file.FullName } 
    } 
} 

注:上述简单地输出的文件的路径删除;将$file.FullName替换为$file.Delete()以执行实际删除。

+0

@ mklement0我还没有听说过EnumerateFiles(),并且预先生成一个截止日期让我感到很蠢,因为之前没有想过它!但我不愿意尝试使用foreach(),因为我工作的目录很大。 foreach()只有在数据大小小于可用内存的情况下才有效吗? – Deusgiggity

+0

@Deusgiggity:不,foreach是可以安全使用的,因为它一次只处理一个项目(类似于ForEach-Object cmdlet,但不同于'.ForEach()'集合运算符,它在一个先前存在的整个收藏)。由于'EnumerateFiles()'也一次生成一个文件信息对象,所以即使对于大型目录,这种方法也应该可以工作。 – mklement0

1

这将删除并行处理中的所有内容。

workflow delOld([string]$dir, [int]$numDays){ 
    $timespan = new-timespan -days $numDays 
    $curTime = get-date 
    $Files = get-childItem $dir -Recurse -file | where-object {(($curTime)-($_.LastWriteTime)) -gt $timespan} 
    foreach -parallel ($file in $files){ 
     Remove-Item $File 
    } 

} 

delOld -dir "C:\Users\AndrewD\Downloads" -numDays 8 

现在,如果它的文件夹了很多尝试这种

相关问题