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

申し訳ありませんが、私が見つかりました。その1つのラインに操作を凝縮それぞれの反復で読みやすい変数に再割り当てするよりも効率的でした。テストの目的で、現在、アイテムの削除が行われています。私はこの時点では、おそらくそれほど高速化できないことを知っていますが、私はそれを1 TBのファイルで実行しているので、すべての操作が重要です。

アドバイスをいただきありがとうございます。

+0

をしようとした場合。私は本当にそれがおそらく仕事を捨てるように設計以外のより速くなるかもしれないか分からないのですか?しかし、そのような再設計は、とにかくスピードの増加を否定するでしょう – pointerless

+0

Log Parserを試しましたか? –

+1

99%の時間が物理ディスクを読み込む 'Get-ChildItem'に費やされるので、スピードを上げる方法があれば、[Everything's API](http://www.voidtools.com)を使ってディスクのMFTを直接読み込みます。/support/everything/sdk /)(時間/日付のインデックス作成を有効にする必要があります)。 – wOxxOm

答えて

1

多くのPowerShellコマンドレットは、同等の.NETよりも低速です。たとえば、代わりに[System.IO.File]::Delete($_.FullName)に電話をかけて、パフォーマンスの違いがあるかどうかを確認できます。 Get-ChildItem =>[System.IO.Directory]::GetFiles(...)と同じです。

これを行うには、それぞれに100,000個の空のテストファイルを持つ2つの一時フォルダを作成する小さなスクリプトを作成します。次に、[System.Diagnostics.StopWatch]でラップされた関数の各バージョンを呼び出します。

いくつかのサンプルコード:PowerShell用の

$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の関数はVerb-Nounという名前が付けられているので、Remove-OldItemsのようなものがこの法案に適合します。 PowerShellと.NETメソッドの領域に滞在

+2

同等の.netメソッドが完全に高速かどうかは、使用方法によって異なります。多くのPowerShellコマンドレットは、パイプライン入力を受け入れて複数のアイテムを操作するように記述されていますが、代わりに 'ForEach-Object'にパイプしてから、個々のアイテムのブロック内でコマンドレットを呼び出します。この方法の問題点は、コマンドレット内のセット/ティアダウンコードがすべてのアイテムに対して実行され、アイテムがパイプされている場合は、一度しか実行されないことです。これは実際にスローダウンする方法とコマンドレットの例の1つですが、すべてがコンテキストに依存しているため、テストは良好です。 – briantist

+0

この回答には、SSD以外のディスク速度(ランダムシーク+読み取り)がPSコマンドレットと.NETメソッドの違いよりも*複数桁遅い*であることは記載されていません。 – wOxxOm

+0

@briantist:同意します。 OPはクイックパーパステストを書くべきです。あなたが実際に本当に両方の機能の内部を知っていない限り、あなたが試してみるまで、決して知りません。 –

5

は、ここにあなたがあなたの機能をスピードアップすることができます方法は次のとおりです。

  • 前まで、一度カットオフタイムスタンプを計算します。

  • foreach声明との組み合わせで[IO.DirectoryInfo]タイプのEnumerateFiles()方法(PSV3 +/.NET4 +)を使用してください。 wOxxOmへの帽子の先端。

    • EnumerateFiles()と同様に、メモリの使用を一定に、維持、ファイルを一つずつを列挙し、より高速なGet-ChildItemより。

      • 警告

        • EnumerateFiles()必ずGet-ChildItemは、デフォルトでは、それらを除外し、のみ-Forceが指定されている場合は、それらを含んでいるのに対し、隠されたファイルが含まれています。
        • によるアクセス権の欠如にアクセスできないディレクトリに遭遇する可能性がある場合、すべてのファイルにアクセスが処理されるであることをことを保証するためにtry/catchブロックにforeach文全体を囲みます。

        • 列挙順序は、Get-ChildItemのものと異なる場合があります。

    • のPowerShellのforeach声明ForEach-Objectレットよりもはるかに高速であり、また、より高速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'はアイテムを一度に1つずつ処理するので安全です(' ForEach-Object'コマンドレットに似ていますが、 '.ForEach()'コレクション演算子とは異なり、既存のコレクション全体)。 'EnumerateFiles()'は一度に1つずつファイル情報オブジェクトも生成するので、この方法は大きなディレクトリでも機能するはずです。 – 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 

フォルダのそのたくさんは、あなたが私の目にそれを作ることができるほど高速だこの

関連する問題