2017-06-05 59 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 

現在,如果它的文件夾了很多嘗試這種

相關問題