スクリプトの目的は次のとおりです。
これまでのところ(3)は難しい部分です。
ここに私がこれまでに書いてテストしたものを示します。これは、100個または1000個のファイルがあるフォルダーで完全に機能します。
$hostname=hostname
$directory = "foo"
$dteCurrentDate = Get-Date –f "yyyy/MM/dd"
$FolderItems = Get-ChildItem $directory -recurse
$Measurement = $FolderItems | Measure-Object -property length -sum
$colitems = $FolderItems | measure-Object -property length -sum
"$hostname;{0:N2}" -f ($colitems.sum / 1MB) + "MB;" + $Measurement.count + " files;" + "$dteCurrentDate"
ただし、数百万のファイルがあるフォルダーでは、$colitems
変数は、数百万のファイルの情報の収集から非常に大きくなり、システムを不安定にします。この情報を描画して保存するより効率的な方法はありますか?
ストリーミングとパイプラインを使用する場合は、(3)の問題を大幅に減らす必要があります。これは、ストリーミングすると、各オブジェクトが使用可能なときにパイプラインに沿って渡され、多くのメモリを消費せず、数百万のファイルを処理します(ただし時間がかかります)。
_Get-ChildItem $directory -recurse | Measure-Object -property length -sum
_
@Stejのステートメント_Get-ChildItem probably reads all entries in the directory and then begins pushing them to the pipeline.
_が正しいとは思わない。パイプライン処理は、PowerShellの基本的な概念です(コマンドレット、スクリプトなどがそれをサポートすることを提供します)。両方とも、処理済みオブジェクトがパイプラインに沿って、利用可能なときに1つずつ渡され、必要なときはonlyも渡されます。 _Get-ChildItem
_の動作は変わりません。
このすばらしい例は、Windows PowerShellパイプラインについてにあります。
それから引用:
Out-Host -Pagingコマンドは、出力を長く表示し、ゆっくり表示したい場合に便利なパイプライン要素です。操作が非常にCPUを集中的に使用する場合に特に役立ちます。表示の準備が完了した完全なページがある場合、処理はOut-Hostコマンドレットに転送されるため、パイプラインで先行するコマンドレットは、出力の次のページが使用可能になるまで操作を停止します。これは、Windowsタスクマネージャーを使用して、Windows PowerShellによるCPUとメモリの使用を監視する場合に確認できます。
次のコマンドを実行します:_
Get-ChildItem C:\Windows -Recurse
_。 CPUとメモリの使用量をこのコマンドと比較してください:_Get-ChildItem C:\Windows -Recurse | Out-Host -Paging
_。
_Get-ChildItem
_で_c:\
_を使用する場合のベンチマーク(約179516個のファイル、数百万個ではなく、十分な数):
_$a = gci c:\ -recurse
_を実行した後(および_$a.count
_を実行した後)のメモリ使用量は_527,332K
_でした。
_gci c:\ -recurse | measure-object
_を実行した後のメモリ使用量は_59,452K
_であり、_80,000K
_を超えることはありませんでした。
(メモリ-プライベートワーキングセット-TaskManagerから、_powershell.exe
_プロセスのメモリを参照。最初は、_22,000K
_についてでした。)
また、200万個のファイルを試してみました(作成に時間がかかりました!)
同様の実験:
_$a = gci c:\ -recurse
_を実行した後(および_$a.count
_を実行した後)のメモリ使用量は_2,808,508K
_でした。
_gci c:\ -recurse | measure-object
_の実行中のメモリ使用量は_308,060K
_であり、_400,000K
_を超えることはありませんでした。終了後、[GC]::Collect()
を実行して_22,000K
_レベルに戻る必要がありました。
_Get-ChildItem
_とパイプライン処理により、数百万のファイルに対してもメモリを大幅に改善できると確信しています。
Get-ChildItem
は、おそらくディレクトリ内のすべてのエントリを読み取り、パイプラインへのプッシュを開始します。その場合は Get-ChildItem
はうまく動作しません。NET4.0に切り替えてEnumerateFiles
とEnumeratedDirectories
を使用してみてください:
function Get-HugeDirStats($directory) {
function go($dir, $stats)
{
foreach ($f in [system.io.Directory]::EnumerateFiles($dir))
{
$stats.Count++
$stats.Size += (New-Object io.FileInfo $f).Length
}
foreach ($d in [system.io.directory]::EnumerateDirectories($dir))
{
go $d $stats
}
}
$statistics = New-Object PsObject -Property @{Count = 0; Size = [long]0 }
go $directory $statistics
$statistics
}
#example
$stats = Get-HugeDirStats c:\windows
ここで最も高価な部分は、New-Object io.FileInfo $f
。これは、EnumerateFiles
がファイル名のみを返すためです。したがって、ファイルの数だけで十分な場合は、行をコメント化できます。
.NET 4.0の使用方法については、スタックオーバーフローの質問。NET 4ランタイムでPowerShellを実行するにはどうすればよいですか?を参照してください。
また、高速ですが、ディレクトリ内のすべてのファイルを読み取るプレーンな古いメソッドを使用することもできます。それはあなたのニーズに依存しますので、試してみてください。後ですべての方法の比較があります。
function Get-HugeDirStats2($directory) {
function go($dir, $stats)
{
foreach ($f in $dir.GetFiles())
{
$stats.Count++
$stats.Size += $f.Length
}
foreach ($d in $dir.GetDirectories())
{
go $d $stats
}
}
$statistics = New-Object PsObject -Property @{Count = 0; Size = [long]0 }
go (new-object IO.DirectoryInfo $directory) $statistics
$statistics
}
比較:
Measure-Command { $stats = Get-HugeDirStats c:\windows }
Measure-Command { $stats = Get-HugeDirStats2 c:\windows }
Measure-Command { Get-ChildItem c:\windows -recurse | Measure-Object -property length -sum }
TotalSeconds : 64,2217378
...
TotalSeconds : 12,5851008
...
TotalSeconds : 20,4329362
...
@manojlds:パイプラインは基本的な概念です。しかし、概念としては、プロバイダーとは関係ありません。ファイルシステムプロバイダーは、遅延評価機能(〜列挙子)を持たない.NET実装(.NET 2.0)に依存しています。自分で確認してください。
次の関数は非常にクールで、フォルダーのサイズをすばやく計算できますが、常に機能するとは限りません(特に、アクセス許可の問題またはフォルダーパスが長すぎる場合)。
Function sizeFolder($path) # Return the size in MB.
{
$objFSO = New-Object -com Scripting.FileSystemObject
("{0:N2}" -f (($objFSO.GetFolder($path).Size) / 1MB))
}