8

フォルダ内の160万個のファイルを調べ、ファイル名に基づいて正しいフォルダに移動するスクリプトを作成しようとしています。

その理由は、NTFSは、パフォーマンスを低下させることなく、単一のフォルダー内の多数のファイルを処理できないためです。

スクリプトは「Get-ChildItem」を呼び出して、そのフォルダー内のすべてのアイテムを取得します。ご想像のとおり、これは大量のメモリ(約3.8 GB)を消費します。

あまりメモリを消費せずにディレクトリ内のすべてのファイルを反復処理する他の方法があるかどうか知りたいです。

4

3 に答える 3

13

もし、するなら

$files = Get-ChildItem $dirWithMillionsOfFiles
#Now, process with $files

あなたは記憶の問題に直面するでしょう。

PowerShell配管を使用してファイルを処理します。

Get-ChildItem $dirWithMillionsOfFiles | %{ 
    #process here
}

2番目の方法は、メモリの消費量が少なく、理想的には特定のポイントを超えて大きくならないようにする必要があります。

于 2012-09-05T04:20:12.110 に答える
13

メモリフットプリントを削減する必要がある場合は、使用をスキップしてGet-ChildItem、代わりに.NETAPIを直接使用できます。Powershell v2を使用していることを前提としています。その場合は、最初にここの手順に従って、 .NET4をPowershellv2にロードできるようにします。

.NET 4には、ファイルとディレクトリを配列で返すのではなく、それらを列挙するための優れたAPIがいくつかあります。

[IO.Directory]::EnumerateFiles("C:\logs") |%{ <move file $_> }

このAPIを使用すると、の代わりに[IO.Directory]::GetFiles()、一度に1つのファイル名のみが処理されるため、メモリ消費量は比較的少なくなります。

編集

また、のような単純なパイプラインアプローチを試したことがあると想定していましたGet-ChildItem |ForEach { process }。これで十分なら、私はそれが進むべき道であることに同意します。

しかし、私はよくある誤解を解き明かしたいと思いGet-ChildItemます。v2(または実際にはファイルシステムプロバイダー)では、実際にはストリーミングされませ。実装ではAPIDirectory.GetDirectoriesDirectory.GetFilesを使用します。この場合、処理が行われる前に1.6M要素の配列が生成されます。これが完了すると、はい、パイプラインの残りの部分がストリーミングされます。FileInfoそして、はい、この最初の低レベルの部分は、リッチオブジェクトの配列ではなく、単なる文字列配列であるため、影響は比較的最小限です。O(1)しかし、このパターンでメモリが使用されていると主張するのは誤りです。

対照的に、Powershellv3は.NET4上に構築されているため、上記(Directory.EnumerateDirectoriesおよびDirectory.EnumerateFiles)で説明したストリーミングAPIを利用します。これは素晴らしい変更であり、あなたと同じようなシナリオで役立ちます。

于 2012-09-05T04:24:14.067 に答える
0

これが、.Net4.0を使用せずに実装した方法です。Powershell 2.0と昔ながらのDIRコマンドのみ:

たった2行の(簡単な)コードです:

cd <source_path>
cmd /c "dir /B"| % { move-item $($_) -destination "<dest_folder>" }

私のPowershellプロセスは15MBしか使用していません。古いWindows2008サーバーに変更はありません。

乾杯!

于 2016-02-25T14:13:45.860 に答える