曖昧な「ストレージが重い・空きがない」という事象は、原因の特定が難航しやすい障害の一つです。巨大ログの突発的増殖、ロックされたファイルによる削除不能、NTFSメタデータの増大など、原因を定量的に特定せずに場当たり的なファイル削除を行っても根本解決には至りません。
Windows / PowerShell環境において、単なるエクスプローラーでの容量確認を超えた「ファイル操作・容量診断レシピ」と、一括で一次切り分けを行う自動化スクリプト『disk-doctor.ps1』を提案します。
1. 階層的容量測定による「闇容量」の可視化
標準の Get-ChildItem に -Recurse を指定して巨大ディレクトリを走査すると、パイプラインのオーバーヘッドにより処理が極めて鈍化します。高速な診断には .NET 列挙 API(EnumerateFiles)をダイレクトに呼び出すか、CIM インスタンスを活用したセグメント解析が必要です。
高速なディレクトリ別容量ランキングの算出
# .NET System.IO APIを利用した高速深層走査とトップ10ディレクトリの抽出
[CmdletBinding()]
param([string]$Path = "C:\")
[System.IO.Directory]::EnumerateDirectories($Path) | ForEach-Object {
$dir = $_
try {
$size = [System.IO.Directory]::EnumerateFiles($dir, "*", [System.IO.SearchOption]::AllDirectories) |
ForEach-Object { (New-Object System.IO.FileInfo($_)).Length } |
Measure-Object -Sum | Select-Object -ExpandProperty Sum
[PSCustomObject]@{
Directory = $dir
SizeGB = [math]::Round($size / 1GB, 2)
}
} catch {
# アクセス権限拒否(UnauthorizedAccessException)等をハンドリング
[PSCustomObject]@{ Directory = $dir; SizeGB = -1 }
}
} | Sort-Object SizeGB -Descending | Select-Object -First 10 | Format-Table -AutoSize
出力例と判定基準
Directory SizeGB
--------- ------
C:\ProgramData 42.15
C:\Users 28.40
C:\Windows 24.10
C:\System Volume Information -1.00
-
SizeGB = -1.00:アクセス拒否(管理者権限での再実行、または
System Volume Informationなどの保護領域)。 - 特定のアプリケーションデータ配下の不自然な増大:ローテーションが破綻したアプリケーションログや、一時ファイルの残留を疑う決定的な根拠となります。
2. I/Oボトルネックとロック状態の特定
容量不足だけでなく「ファイルが削除できない」「特定ディレクトリへの書き込みでI/O待ちが発生する」現象は、プロセスによるファイルロックやNTFSフラグメンテーションが起因します。
ハンドルロックの特定とNTFSメタデータの状態調査
# 1. オープン中のファイルとプロセスの紐付け(OpenFilesまたはGet-Processのコンテキスト利用)
# ※ OpenFiles を有効化(要再起動)している環境での直接照会
openfiles /query /fo csv | ConvertFrom-Csv | Select-Object -First 5
# 2. ボリュームの断片化およびUSNジャーナル(変更履歴)の検査
fsutil usn queryjournal C:
| 取得パラメータ / コマンド | 意味と診断時の判定基準 |
|---|---|
Usn Journal 情報 |
LowestValidUsn と NextUsn の差分が極端に大きい場合、激しいファイル生成・削除がバックグラウンドで行われている。 |
openfiles |
特定のログファイルがどの PID(プロセスID)に掴まれているかを特定。強制削除ではなくプロセス側の安全な停止手順へ導く。 |
Get-Volume (AllocationUnitSize) |
クラスタサイズ(通常4KB)。数バイトの微小ファイルが数百万個生成される環境では、ディスク上のサイズ(Allocated Size)が肥大化する「セクタロス」が発生。 |
3. 障害時にワンショットで一次切り分けを行う「自動化PowerShellスクリプト」
緊急時にストレージの全体像(パーティション状態、巨大ファイル上位20件、一時フォルダ肥大化)を網羅的に取得し、ログファイルへ保存する統合診断レシピです。
ワンステップ診断スクリプト:disk-doctor.ps1
<#
.SYNOPSIS
Windows/PowerShell用 ディスク・ファイルシステム一次診断スクリプト
#>
[CmdletBinding()]
param (
[string]$TargetDrive = "C:",
[string]$LogPath = "$env:TEMP\disk_diag_$(Get-Date -Format 'yyyyMMdd_HHmmss').log"
)
# 出力のリダイレクト設定
Start-Transcript -Path $LogPath -Append | Out-Null
Write-Host "==========================================" -ForegroundColor Cyan
Write-Host " Storage Diagnostic Report - $(Get-Date)" -ForegroundColor Cyan
Write-Host " Host: $env:COMPUTERNAME ($((Get-CimInstance Win32_OperatingSystem).Caption))" -ForegroundColor Cyan
Write-Host " Target Drive: $TargetDrive" -ForegroundColor Cyan
Write-Host "==========================================" -ForegroundColor Cyan
Write-Host "`n[1. Volume & Partition Status]" -ForegroundColor Yellow
Get-Volume -DriveLetter $TargetDrive.TrimEnd(':') | Format-Table DriveLetter, FileSystemLabel, FileSystem, SizeRemainingGB, SizeGB, HealthStatus
Write-Host "`n[2. Top 15 Largest Files in $TargetDrive\]" -ForegroundColor Yellow
try {
Get-ChildItem -Path "$TargetDrive\" -Recurse -File -ErrorAction SilentlyContinue |
Sort-Object Length -Descending |
Select-Object -First 15 |
Select-Object @{N="FullName";E={$_.FullName}}, @{N="SizeMB";E={[math]::Round($_.Length / 1MB, 2)}}, LastWriteTime |
Format-Table -AutoSize
} catch {
Write-Warning "Failed to scan files completely due to access restrictions."
}
Write-Host "`n[3. System Temp Directories Size Summary]" -ForegroundColor Yellow
$tempPaths = @($env:TEMP, "C:\Windows\Temp", "C:\Windows\Prefetch")
foreach ($path in $tempPaths) {
if (Test-Path $path) {
$measure = Get-ChildItem -Path $path -Recurse -File -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum
$sizeMB = [math]::Round(($measure.Sum / 1MB), 2)
[PSCustomObject]@{
TempPath = $path
FileCount = $measure.Count
TotalMB = $sizeMB
} | Format-Table -AutoSize
}
}
Write-Host "`n[4. NTFS/File System Error Logs (System EventLog)]" -ForegroundColor Yellow
Get-WinEvent -FilterHashtable @{LogName='System'; ProviderName='Ntfs','Disk'; StartTime=(Get-Date).AddDays(-1)} -ErrorAction SilentlyContinue |
Select-Object TimeCreated, Id, LevelDisplayName, Message -First 5 |
Format-Table -AutoSize
Write-Host "==========================================" -ForegroundColor Cyan
Write-Host "Diagnostic complete. Saved to: $LogPath" -ForegroundColor Cyan
Stop-Transcript | Out-Null
実行方法
# 実行ポリシーを一時許可して診断を実行
Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope Process -Force
.\disk-doctor.ps1 -TargetDrive "C:"
決定論的アプローチによるストレージ管理
「容量が足りないから適当にログを消す」という対処療法は、根本原因(ロギング設計の破綻、ハンドルリーク、特定プロセスによるI/O占有)の隠蔽につながります。
-
L1(物理・ボリューム層):
Get-Volumeによるファイルシステム整合性(HealthStatus)とアロケーション単位の検証 - L2(ファイルツリー構造層): .NET 列挙 API によるミリ秒単位の巨大ディレクトリ特定
-
L3(カーネル・プロセス層):
fsutilおよびGet-WinEventによるI/Oエラー・変更履歴ログの解析
PowerShellの持つ強力な.NET相互運用性とOS管理API(CIM/WMI)を組み合わせることで、直感に頼らない再現可能な障害解析が可能になります。